From 19e33b0508b96fdf1fb38da514a643a663a8c180 Mon Sep 17 00:00:00 2001 From: Samer Albahra Date: Mon, 4 May 2015 17:53:21 -0500 Subject: [PATCH 01/16] Support changing the user for launch script --- OpenSprinkler.launch | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenSprinkler.launch b/OpenSprinkler.launch index d51ff22f..ad7e1706 100755 --- a/OpenSprinkler.launch +++ b/OpenSprinkler.launch @@ -32,6 +32,7 @@ DAEMON_ARGS="" HOMEDIR=__OpenSprinkler_Path__ PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME +USER=root # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 @@ -56,9 +57,9 @@ do_start() # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started - start-stop-daemon --start --quiet --chdir $HOMEDIR --pidfile $PIDFILE --make-pidfile --background --exec $DAEMON --test > /dev/null \ + start-stop-daemon --start --quiet --chuid $USER --chdir $HOMEDIR --pidfile $PIDFILE --make-pidfile --background --exec $DAEMON --test > /dev/null \ || return 1 - start-stop-daemon --start --quiet --chdir $HOMEDIR --pidfile $PIDFILE --make-pidfile --background --exec $DAEMON -- \ + start-stop-daemon --start --quiet --chuid $USER --chdir $HOMEDIR --pidfile $PIDFILE --make-pidfile --background --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 } From 1d786b0cd718ccd9f2878ecc0ad71c30b78d7756 Mon Sep 17 00:00:00 2001 From: Rayshobby Date: Sun, 10 May 2015 14:52:22 -0400 Subject: [PATCH 02/16] slightly better way to insert delays to handle sending multiple packets --- server.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server.cpp b/server.cpp index 9daef471..7a0f297a 100644 --- a/server.cpp +++ b/server.cpp @@ -198,7 +198,6 @@ void send_packet(bool final=false) { ether.httpServerReply_with_flags(bfill.position(), TCP_FLAGS_ACK_V, 3); bfill=ether.tcpOffset(); } - delay(1); // for Arduino we need to insert a slight delay to allow packet to be sent out } int available_ether_buffer() { @@ -399,6 +398,7 @@ void server_json_stations_main() } } bfill.emit_p(PSTR("],\"maxlen\":$D}"), STATION_NAME_SIZE); + delay(1); } /** Output station names and attributes */ @@ -779,6 +779,7 @@ void server_json_controller_main() { } bfill.emit_p(PSTR("[0,0]],\"lrun\":[$D,$D,$D,$L]}"), pd.lastrun.station, pd.lastrun.program, pd.lastrun.duration, pd.lastrun.endtime); + delay(1); } @@ -1338,6 +1339,7 @@ byte streamfile (char* name) { //send a file to the buffer break; } } + delay(1); //ether.httpServerReply_with_flags(cur, TCP_FLAGS_ACK_V+TCP_FLAGS_FIN_V, 3); send_packet(true); From 3f32a564e278f32ebfd3aae7efa15769f10e83f2 Mon Sep 17 00:00:00 2001 From: Rayshobby Date: Fri, 22 May 2015 01:07:05 -0400 Subject: [PATCH 03/16] change the way interval checking is done, to avoid issues with negative values; add timeout to EtherCard's initialize funciton, to avoid dead loop --- enc28j60.cpp | 9 ++++++--- main.cpp | 13 +++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/enc28j60.cpp b/enc28j60.cpp index 7075467f..14e25549 100644 --- a/enc28j60.cpp +++ b/enc28j60.cpp @@ -377,9 +377,12 @@ byte ENC28J60::initialize (uint16_t size, const byte* macaddr, byte csPin) { writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); delay(2); // errata B7/2 - while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) - ; - + + unsigned long st = millis(); + while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) { + if (millis() > st+30000L) return 0; // 30 seconds timeout + } + gNextPacketPtr = RXSTART_INIT; writeReg(ERXST, RXSTART_INIT); writeReg(ERXRDPT, RXSTART_INIT); diff --git a/main.cpp b/main.cpp index 1eed7704..380ce62c 100644 --- a/main.cpp +++ b/main.cpp @@ -192,6 +192,7 @@ void do_setup() { MCUSR &= ~(1<0 || os.status.program_busy) return; ulong ntz = os.now_tz(); - if (os.checkwt_success_lasttime && (ntz - os.checkwt_success_lasttime > CHECK_WEATHER_SUCCESS_INTERVAL)) { + if (os.checkwt_success_lasttime && os.checkwt_lasttime && (ntz > os.checkwt_success_lasttime + CHECK_WEATHER_SUCCESS_INTERVAL)) { // if weather check has failed to return for too long, restart network os.network_lasttime = 0; return; } - if (!os.checkwt_lasttime || ((ntz - os.checkwt_lasttime) > CHECK_WEATHER_INTERVAL)) { + if (!os.checkwt_lasttime || (ntz > os.checkwt_lasttime + CHECK_WEATHER_INTERVAL)) { os.checkwt_lasttime = os.now_tz(); GetWeather(); } @@ -793,7 +794,7 @@ void log_statistics(time_t curr_time) { static byte stat_n = 0; static ulong stat_lasttime = 0; // update statistics once 15 minutes - if (curr_time - stat_lasttime > STAT_UPDATE_INTERVAL) { + if (curr_time > stat_lasttime + STAT_UPDATE_INTERVAL) { stat_lasttime = curr_time; ulong wp_total = os.water_percent_avg; wp_total = wp_total * stat_n; @@ -936,7 +937,7 @@ void check_network() { // the more time it fails, the longer the gap between two checks ulong interval = 1 << (os.status.network_fails); interval *= CHECK_NETWORK_INTERVAL; - if (now() - os.network_lasttime > interval) { + if (now() > os.network_lasttime + interval) { // change LCD icon to indicate it's checking network if (!ui_state) { os.lcd.setCursor(15, 1); @@ -965,7 +966,7 @@ void check_network() { } else os.status.network_fails=0; // if failed more than once, reconnect - if ((os.status.network_fails>2 || (now() - os.dhcpnew_lasttime > DHCP_RENEW_INTERVAL))) { + if (os.status.network_fails>2 || (now() > os.dhcpnew_lasttime + DHCP_RENEW_INTERVAL)) { os.dhcpnew_lasttime = now(); //os.lcd_print_line_clear_pgm(PSTR(""),0); if (os.start_network()) @@ -983,7 +984,7 @@ void perform_ntp_sync() { // do not perform sync if this option is disabled, or if network is not available, or if a program is running if (!os.options[OPTION_USE_NTP].value || os.status.network_fails>0 || os.status.program_busy) return; - if (os.ntpsync_lasttime == 0 || (os.now_tz() - os.ntpsync_lasttime > NTP_SYNC_INTERVAL)) { + if (os.ntpsync_lasttime == 0 || (os.now_tz() > os.ntpsync_lasttime + NTP_SYNC_INTERVAL)) { os.ntpsync_lasttime = os.now_tz(); if (!ui_state) { os.lcd_print_line_clear_pgm(PSTR("NTP Syncing..."),1); From da01a6200d9f929ee104663475476168542daf4c Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Jun 2015 17:13:11 -0400 Subject: [PATCH 04/16] add support for I2C LCD; add support for saving weather options --- LiquidCrystal.cpp | 369 ++++++++++++++++++++++++++++------------------ LiquidCrystal.h | 61 +++++--- OpenSprinkler.cpp | 64 ++++++-- OpenSprinkler.h | 2 + defines.h | 18 ++- server.cpp | 100 ++++--------- utils.cpp | 77 +++++++++- utils.h | 3 +- 8 files changed, 433 insertions(+), 261 deletions(-) diff --git a/LiquidCrystal.cpp b/LiquidCrystal.cpp index 0653487d..fac6d0c2 100644 --- a/LiquidCrystal.cpp +++ b/LiquidCrystal.cpp @@ -1,9 +1,7 @@ #include "LiquidCrystal.h" - -#include -#include #include -#include "Arduino.h" +#include +#include // When the display powers up, it is configured as follows: // @@ -24,30 +22,125 @@ // can't assume that its in that state when a sketch starts (and the // LiquidCrystal constructor is called). -LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, - uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) -{ - init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7); -} - -LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, - uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) -{ - init(0, rs, 255, enable, d0, d1, d2, d3, d4, d5, d6, d7); -} - -LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) -{ - init(1, rs, rw, enable, d0, d1, d2, d3, 0, 0, 0, 0); -} - -LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) -{ - init(1, rs, 255, enable, d0, d1, d2, d3, 0, 0, 0, 0); +void LiquidCrystal::begin() { + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + if (_type == LCD_I2C) { + _displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 + delay(50); + + // Now we pull both RS and R/W low to begin commands + expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1) + delay(1000); + + //put the LCD into 4 bit mode + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + write4bits(0x03 << 4); + delayMicroseconds(4500); // wait min 4.1ms + + // second try + write4bits(0x03 << 4); + delayMicroseconds(4500); // wait min 4.1ms + + // third go! + write4bits(0x03 << 4); + delayMicroseconds(150); + + // finally, set to 4-bit interface + write4bits(0x02 << 4); + + // set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear it off + clear(); + + // Initialize to default text direction (for roman languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); + + home(); + } + #endif + + if (_type == LCD_STD) { + _displayfunction |= LCD_2LINE; + _numlines = 2; + _currline = 0; + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 + delayMicroseconds(50000); + // Now we pull both RS and R/W low to begin commands + digitalWrite(_rs_pin, LOW); + digitalWrite(_enable_pin, LOW); + if (_rw_pin != 255) { + digitalWrite(_rw_pin, LOW); + } + + //put the LCD into 4 bit or 8 bit mode + if (! (_displayfunction & LCD_8BITMODE)) { + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + write4bits(0x03); + delayMicroseconds(4500); // wait min 4.1ms + + // second try + write4bits(0x03); + delayMicroseconds(4500); // wait min 4.1ms + + // third go! + write4bits(0x03); + delayMicroseconds(150); + + // finally, set to 4-bit interface + write4bits(0x02); + } else { + // this is according to the hitachi HD44780 datasheet + // page 45 figure 23 + + // Send function set command sequence + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(4500); // wait more than 4.1ms + + // second try + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(150); + + // third go + command(LCD_FUNCTIONSET | _displayfunction); + } + + // finally, set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear it off + clear(); + + // Initialize to default text direction (for romance languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); + } } void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable, @@ -66,101 +159,42 @@ void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t en _data_pins[5] = d5; _data_pins[6] = d6; _data_pins[7] = d7; - - pinMode(_rs_pin, OUTPUT); - // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin# - if (_rw_pin != 255) { - pinMode(_rw_pin, OUTPUT); - } - pinMode(_enable_pin, OUTPUT); - - if (fourbitmode) - _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; - else - _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS; - begin(16, 1); -} - -void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { - if (lines > 1) { - _displayfunction |= LCD_2LINE; - } - _numlines = lines; - _currline = 0; - - // for some 1 line displays you can select a 10 pixel high font - if ((dotsize != 0) && (lines == 1)) { - _displayfunction |= LCD_5x10DOTS; - } - - // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! - // according to datasheet, we need at least 40ms after power rises above 2.7V - // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 - delayMicroseconds(50000); - // Now we pull both RS and R/W low to begin commands - digitalWrite(_rs_pin, LOW); - digitalWrite(_enable_pin, LOW); - if (_rw_pin != 255) { - digitalWrite(_rw_pin, LOW); - } + Wire.begin(); + _type = LCD_STD; + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + // detect I2C and assign _type variable accordingly + Wire.beginTransmission(LCD_I2C_ADDR); + Wire.write(0x00); + uint8_t ret = Wire.endTransmission(); + if (!ret) _type = LCD_I2C; - //put the LCD into 4 bit or 8 bit mode - if (! (_displayfunction & LCD_8BITMODE)) { - // this is according to the hitachi HD44780 datasheet - // figure 24, pg 46 - - // we start in 8bit mode, try to set 4 bit mode - write4bits(0x03); - delayMicroseconds(4500); // wait min 4.1ms - - // second try - write4bits(0x03); - delayMicroseconds(4500); // wait min 4.1ms + if (_type == LCD_I2C) { + _addr = LCD_I2C_ADDR; + _cols = 16; + _rows = 2; + _charsize = LCD_5x8DOTS; + _backlightval = LCD_BACKLIGHT; + } + #endif + + if (_type == LCD_STD) { + pinMode(_rs_pin, OUTPUT); + // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin# + if (_rw_pin != 255) { + pinMode(_rw_pin, OUTPUT); + } + pinMode(_enable_pin, OUTPUT); - // third go! - write4bits(0x03); - delayMicroseconds(150); - - // finally, set to 4-bit interface - write4bits(0x02); - } else { - // this is according to the hitachi HD44780 datasheet - // page 45 figure 23 - - // Send function set command sequence - command(LCD_FUNCTIONSET | _displayfunction); - delayMicroseconds(4500); // wait more than 4.1ms - - // second try - command(LCD_FUNCTIONSET | _displayfunction); - delayMicroseconds(150); - - // third go - command(LCD_FUNCTIONSET | _displayfunction); } - - // finally, set # lines, font size, etc. - command(LCD_FUNCTIONSET | _displayfunction); - - // turn the display on with no cursor or blinking default - _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; - display(); - - // clear it off - clear(); - - // Initialize to default text direction (for romance languages) - _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; - // set the entry mode - command(LCD_ENTRYMODESET | _displaymode); + _displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; } /********** high level commands, for the user! */ void LiquidCrystal::clear() { - command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero + command(LCD_CLEARDISPLAY);// clear display, set cursor position to zero delayMicroseconds(2000); // this command takes a long time! } @@ -173,10 +207,18 @@ void LiquidCrystal::home() void LiquidCrystal::setCursor(uint8_t col, uint8_t row) { int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; - if ( row >= _numlines ) { - row = _numlines-1; // we count rows starting w/0 + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + if (_type == LCD_I2C) { + if (row > _rows) { + row = _rows-1; // we count rows starting w/0 + } } - + #endif + if (_type == LCD_STD) { + if (row >= _numlines) { + row = _numlines-1; + } + } command(LCD_SETDDRAMADDR | (col + row_offsets[row])); } @@ -252,36 +294,88 @@ void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) { } } +#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) +// Turn the (optional) backlight off/on +void LiquidCrystal::noBacklight(void) { + _backlightval=LCD_NOBACKLIGHT; + expanderWrite(0); +} + +void LiquidCrystal::backlight(void) { + _backlightval=LCD_BACKLIGHT; + expanderWrite(0); +} +#endif + /*********** mid level commands, for sending data/cmds */ inline void LiquidCrystal::command(uint8_t value) { - send(value, LOW); + send(value, 0); } inline size_t LiquidCrystal::write(uint8_t value) { - send(value, HIGH); + send(value, Rs); return 1; // assume sucess } /************ low level data pushing commands **********/ -// write either command or data, with automatic 4/8-bit selection +// write either command or data void LiquidCrystal::send(uint8_t value, uint8_t mode) { - digitalWrite(_rs_pin, mode); - - // if there is a RW pin indicated, set it low to Write - if (_rw_pin != 255) { - digitalWrite(_rw_pin, LOW); - } - - if (_displayfunction & LCD_8BITMODE) { - write8bits(value); - } else { + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + if (_type == LCD_I2C) { + uint8_t highnib=value&0xf0; + uint8_t lownib=(value<<4)&0xf0; + write4bits((highnib)|mode); + write4bits((lownib)|mode); + } + #endif + if (_type == LCD_STD) { + digitalWrite(_rs_pin, mode); + + // if there is a RW pin indicated, set it low to Write + if (_rw_pin != 255) { + digitalWrite(_rw_pin, LOW); + } + write4bits(value>>4); write4bits(value); } } +void LiquidCrystal::write4bits(uint8_t value) { + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + if (_type == LCD_I2C) { + expanderWrite(value); + pulseEnable(value); + } + #endif + if (_type == LCD_STD) { + for (int i = 0; i < 4; i++) { + pinMode(_data_pins[i], OUTPUT); + digitalWrite(_data_pins[i], (value >> i) & 0x01); + } + + pulseEnable(); + } +} + +#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) +void LiquidCrystal::expanderWrite(uint8_t _data){ + Wire.beginTransmission(_addr); + Wire.write((int)(_data) | _backlightval); + Wire.endTransmission(); +} + +void LiquidCrystal::pulseEnable(uint8_t _data){ + expanderWrite(_data | En); // En high + delayMicroseconds(1); // enable pulse must be >450ns + + expanderWrite(_data & ~En); // En low + delayMicroseconds(50); // commands need > 37us to settle +} +#endif + void LiquidCrystal::pulseEnable(void) { digitalWrite(_enable_pin, LOW); delayMicroseconds(1); @@ -291,20 +385,3 @@ void LiquidCrystal::pulseEnable(void) { delayMicroseconds(100); // commands need > 37us to settle } -void LiquidCrystal::write4bits(uint8_t value) { - for (int i = 0; i < 4; i++) { - pinMode(_data_pins[i], OUTPUT); - digitalWrite(_data_pins[i], (value >> i) & 0x01); - } - - pulseEnable(); -} - -void LiquidCrystal::write8bits(uint8_t value) { - for (int i = 0; i < 8; i++) { - pinMode(_data_pins[i], OUTPUT); - digitalWrite(_data_pins[i], (value >> i) & 0x01); - } - - pulseEnable(); -} diff --git a/LiquidCrystal.h b/LiquidCrystal.h index 2b897059..e99aa94c 100644 --- a/LiquidCrystal.h +++ b/LiquidCrystal.h @@ -1,8 +1,8 @@ -#ifndef LiquidCrystal_h -#define LiquidCrystal_h +#ifndef LIQUID_CRYSTAL_DUAL_H +#define LIQUID_CRYSTAL_DUAL_H #include -#include "Print.h" +#include // commands #define LCD_CLEARDISPLAY 0x01 @@ -42,25 +42,26 @@ #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 +// flags for backlight control +#define LCD_BACKLIGHT 0x08 +#define LCD_NOBACKLIGHT 0x00 + +#define En B00000100 // Enable bit +#define Rw B00000010 // Read/Write bit +#define Rs B00000001 // Register select bit + +#define LCD_STD 0 // Standard LCD +#define LCD_I2C 1 // I2C LCD +#define LCD_I2C_ADDR 0x27 + class LiquidCrystal : public Print { public: LiquidCrystal() {} - LiquidCrystal(uint8_t rs, uint8_t enable, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, - uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); - LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, - uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); - LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3); - LiquidCrystal(uint8_t rs, uint8_t enable, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3); - void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, - uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); - - void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS); + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); + + void begin(); void clear(); void home(); @@ -82,14 +83,30 @@ class LiquidCrystal : public Print { void setCursor(uint8_t, uint8_t); virtual size_t write(uint8_t); void command(uint8_t); - - using Print::write; + + inline uint8_t type() { return _type; } + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + void noBacklight(); + void backlight(); + #endif + + using Print::write; private: void send(uint8_t, uint8_t); void write4bits(uint8_t); - void write8bits(uint8_t); void pulseEnable(); + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + void expanderWrite(uint8_t); + void pulseEnable(uint8_t); + uint8_t _addr; + uint8_t _cols; + uint8_t _rows; + uint8_t _charsize; + uint8_t _backlightval; + #endif + + uint8_t _type; // LCD type. 0: standard; 1: I2C uint8_t _rs_pin; // LOW: command. HIGH: character. uint8_t _rw_pin; // LOW: write to LCD. HIGH: read from LCD. uint8_t _enable_pin; // activated by a HIGH pulse. @@ -104,4 +121,4 @@ class LiquidCrystal : public Print { uint8_t _numlines,_currline; }; -#endif +#endif // LIQUID_CRYSTAL_DUAL_H diff --git a/OpenSprinkler.cpp b/OpenSprinkler.cpp index 95fba2ba..11447855 100644 --- a/OpenSprinkler.cpp +++ b/OpenSprinkler.cpp @@ -28,6 +28,7 @@ NVConData OpenSprinkler::nvdata; ConStatus OpenSprinkler::status; ConStatus OpenSprinkler::old_status; +byte OpenSprinkler::hw_type; byte OpenSprinkler::nboards; byte OpenSprinkler::nstations; @@ -334,7 +335,7 @@ byte OpenSprinkler::start_network() { #include void OpenSprinkler::reboot_dev() { #if defined(DEMO) - // do nothing + // do nothingb #else sync(); // add sync to prevent file corruption reboot(RB_AUTOBOOT); @@ -343,6 +344,24 @@ void OpenSprinkler::reboot_dev() { #endif // end network init functions +#if defined(ARDUINO) +void OpenSprinkler::lcd_start() { + // turn on lcd + lcd.init(1, PIN_LCD_RS, 255, PIN_LCD_EN, PIN_LCD_D4, PIN_LCD_D5, PIN_LCD_D6, PIN_LCD_D7, 0,0,0,0); + lcd.begin(); + + if (lcd.type() == LCD_STD) { + // set PWM frequency for adjustable LCD backlight and contrast + TCCR1B = 0x01; + // turn on LCD backlight and contrast + pinMode(PIN_LCD_BACKLIGHT, OUTPUT); + pinMode(PIN_LCD_CONTRAST, OUTPUT); + lcd_set_brightness(); + lcd_set_contrast(); + } +} +#endif + void OpenSprinkler::begin() { // shift register setup @@ -402,25 +421,44 @@ void OpenSprinkler::begin() { /*pinMode(PIN_RELAY, OUTPUT); digitalWrite(PIN_RELAY, LOW);*/ -#if defined(ARDUINO) // AVR LCD functions +#if defined(ARDUINO) // AVR SD and LCD functions // set sd cs pin high to release SD pinMode(PIN_SD_CS, OUTPUT); digitalWrite(PIN_SD_CS, HIGH); - // set PWM frequency for adjustable LCD backlight and contrast - TCCR1B = 0x01; - // turn on LCD backlight and contrast - pinMode(PIN_LCD_BACKLIGHT, OUTPUT); - pinMode(PIN_LCD_CONTRAST, OUTPUT); - lcd_set_brightness(); - lcd_set_contrast(); - // turn on lcd - lcd.init(1, PIN_LCD_RS, 255, PIN_LCD_EN, PIN_LCD_D4, PIN_LCD_D5, PIN_LCD_D6, PIN_LCD_D7, 0,0,0,0); - lcd.begin(16, 2); - // Init I2C Wire.begin(); + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) // OS 2.3 specific detections + uint8_t ret; + + // detect hardware type + hw_type = HW_TYPE_AC; + Wire.beginTransmission(MAC_CTRL_ID); + Wire.write(0x00); + ret = Wire.endTransmission(); + if (!ret) { + Wire.requestFrom(MAC_CTRL_ID, 1); + while(!Wire.available()); + ret = Wire.read(); + if (hw_type == HW_TYPE_AC || hw_type == HW_TYPE_DC || hw_type == HW_TYPE_LATCH) { + hw_type = ret; + } else { + // hardware type is not assigned + } + } + + if (hw_type == HW_TYPE_DC) { + pinMode(PIN_BOOST, OUTPUT); + digitalWrite(PIN_BOOST, LOW); + + pinMode(PIN_BOOST_EN, OUTPUT); + digitalWrite(PIN_BOOST_EN, LOW); + } + #endif + + lcd_start(); + // define lcd custom icons byte lcd_wifi_char[8] = { B00000, diff --git a/OpenSprinkler.h b/OpenSprinkler.h index 42325b0a..16aede4e 100644 --- a/OpenSprinkler.h +++ b/OpenSprinkler.h @@ -93,6 +93,7 @@ class OpenSprinkler { static ConStatus status; static ConStatus old_status; static byte nboards, nstations; + static byte hw_type; // hardware type static OptionStruct options[]; // option values, max, name, and flag @@ -182,6 +183,7 @@ class OpenSprinkler { private: static void lcd_print_option(int i); // print an option to the lcd static void lcd_print_2digit(int v); // print a integer in 2 digits + static void lcd_start(); static byte button_read_busy(byte pin_butt, byte waitmode, byte butt, byte is_holding); #endif // LCD functions }; diff --git a/defines.h b/defines.h index 00b17513..fc787a9c 100644 --- a/defines.h +++ b/defines.h @@ -25,19 +25,27 @@ #define _DEFINES_H /** Firmware version, hardware version, and maximal values */ -#define OS_FW_VERSION 214 // Firmware version: 214 means 2.1.4 +#define OS_FW_VERSION 215 // Firmware version: 215 means 2.1.5 // if this number is different from the one stored in non-volatile memory // a device reset will be automatically triggered +/** Hardware version base numbers */ #define OS_HW_VERSION_BASE 0x00 #define OSPI_HW_VERSION_BASE 0x40 #define OSBO_HW_VERSION_BASE 0x80 #define SIM_HW_VERSION_BASE 0xC0 +/** Hardware type macro defines */ +#define HW_TYPE_AC 0xAC // standard 24VAC for 24VAC solenoids only, with triacs +#define HW_TYPE_DC 0xDC // DC powered, for both DC and 24VAC solenoids, with boost converter and MOSFETs +#define HW_TYPE_LATCH 0x1A // DC powered, for DC latching solenoids only, with boost converter and H-bridges + #define MAX_EXT_BOARDS 5 // maximum number of exp. boards (each expands 8 stations) #define MAX_NUM_STATIONS ((1+MAX_EXT_BOARDS)*8) // maximum number of stations +#define WEATHER_OPTS_FILENAME "wtopts.txt" // the file where weather options are stored + /** Non-volatile memory (NVM) defines */ #if defined(ARDUINO) @@ -184,6 +192,8 @@ typedef enum { #define PIN_SR_DATA 21 // shift register data pin #define PIN_SR_CLOCK 22 // shift register clock pin #define PIN_SR_OE 1 // shift register output enable pin + + // regular 16x2 LCD pin defines #define PIN_LCD_RS 19 // LCD rs pin #define PIN_LCD_EN 18 // LCD enable pin #define PIN_LCD_D4 20 // LCD d4 pin @@ -192,10 +202,14 @@ typedef enum { #define PIN_LCD_D7 23 // LCD d7 pin #define PIN_LCD_BACKLIGHT 12 // LCD backlight pin #define PIN_LCD_CONTRAST 13 // LCD contrast pin + + // DC controller pin defines + #define PIN_BOOST 20 // booster pin + #define PIN_BOOST_EN 23 // boost voltage enable pin + #define PIN_ETHER_CS 4 // Ethernet controller chip select pin #define PIN_SD_CS 0 // SD card chip select pin #define PIN_RAINSENSOR 11 // rain sensor is connected to pin D3 - //#define PIN_RELAY 14 // mini relay is connected to pin D14, relay support is not retired #define PIN_EXP_SENSE 4 // expansion board sensing pin (A4) // Ethernet buffer size diff --git a/server.cpp b/server.cpp index 7a0f297a..a3c4ff92 100644 --- a/server.cpp +++ b/server.cpp @@ -96,16 +96,6 @@ static prog_uchar htmlAccessControl[] PROGMEM = "Access-Control-Allow-Origin: *\r\n" ; -static prog_uchar htmlContentCgz[] PROGMEM = - "Content-Type: text/css;charset=utf-8\r\n" - "Content-Encoding: gzip\r\n" -; - -static prog_uchar htmlContentJgz[] PROGMEM = - "Content-Type: text/javascript;charset=utf-8\r\n" - "Content-Encoding: gzip\r\n" -; - static prog_uchar htmlContentJSON[] PROGMEM = "Content-Type: application/json\r\n" "Connection: close\r\n" @@ -746,7 +736,7 @@ void server_json_controller_main() { byte bid, sid; ulong curr_time = os.now_tz(); //os.nvm_string_get(ADDR_NVM_LOCATION, tmp_buffer); - bfill.emit_p(PSTR("\"devt\":$L,\"nbrd\":$D,\"en\":$D,\"rd\":$D,\"rs\":$D,\"rdst\":$L,\"loc\":\"$E\",\"wtkey\":\"$E\",\"sunrise\":$D,\"sunset\":$D,\"eip\":$L,\"lwc\":$L,\"lswc\":$L,\"sbits\":["), + bfill.emit_p(PSTR("\"devt\":$L,\"nbrd\":$D,\"en\":$D,\"rd\":$D,\"rs\":$D,\"rdst\":$L,\"loc\":\"$E\",\"wtkey\":\"$E\",\"sunrise\":$D,\"sunset\":$D,\"eip\":$L,\"lwc\":$L,\"lswc\":$L,\"lrun\":[$D,$D,$D,$L],\"sbits\":["), curr_time, os.nboards, os.status.enabled, @@ -759,7 +749,11 @@ void server_json_controller_main() { os.nvdata.sunset_time, os.external_ip, os.checkwt_lasttime, - os.checkwt_success_lasttime); + os.checkwt_success_lasttime, + pd.lastrun.station, + pd.lastrun.program, + pd.lastrun.duration, + pd.lastrun.endtime); // print sbits for(bid=0;bid 0) { rem = (curr_time >= pd.scheduled_start_time[sid]) ? (pd.scheduled_stop_time[sid]-curr_time) : (pd.scheduled_stop_time[sid]-pd.scheduled_start_time[sid]); } - bfill.emit_p(PSTR("[$D,$L,$L],"), pd.scheduled_program_index[sid], rem, pd.scheduled_start_time[sid]); + bfill.emit_p(PSTR("[$D,$L,$L]"), pd.scheduled_program_index[sid], rem, pd.scheduled_start_time[sid]); + bfill.emit_p((sid=ETHER_BUFFER_SIZE - 54) { - //ether.httpServerReply_with_flags(cur,TCP_FLAGS_ACK_V, 3); - send_packet(); - cur=0; - } else { - break; - } - } - delay(1); - //ether.httpServerReply_with_flags(cur, TCP_FLAGS_ACK_V+TCP_FLAGS_FIN_V, 3); - send_packet(true); - - myfile.close(); - return 1; -} -#else -byte streamfile (char* name) { - return 1; -} -#endif - // analyze the current url void analyze_get_url(char *p) { @@ -1423,21 +1384,12 @@ void analyze_get_url(char *p) } } - if((i==sizeof(urls)/sizeof(URLStruct)) && os.status.has_sd) { - // no server funtion found, file handler - byte k=0; - while (str[k]!=' ' && k<32) {tmp_buffer[k]=str[k];k++;}//search the end, indicated by space - tmp_buffer[k]=0; - // change dir to root - if (streamfile ((char *)tmp_buffer)==0) { - // file not found - print_json_header(); - bfill.emit_p(PSTR("{\"result\":$D}"), HTML_PAGE_NOT_FOUND); - send_packet(true); - } - } else { - send_packet(true); + if((i==sizeof(urls)/sizeof(URLStruct))) { + // no server funtion found + print_json_header(); + bfill.emit_p(PSTR("{\"result\":$D}"), HTML_PAGE_NOT_FOUND); } + send_packet(true); } //delay(50); // add a bit of delay here } diff --git a/utils.cpp b/utils.cpp index 4450197c..fe2080a4 100644 --- a/utils.cpp +++ b/utils.cpp @@ -24,10 +24,48 @@ #include "utils.h" #include "OpenSprinkler.h" extern OpenSprinkler os; -#if defined(ARDUINO) +extern char tmp_buffer[]; + +#if defined(ARDUINO) // AVR #include +#include "SdFat.h" +extern SdFat sd; + +void write_to_file(const char *name, const char *data) { + if (!os.status.has_sd) return; + + char *fn = tmp_buffer+TMP_BUFFER_SIZE-12; + strcpy_P(fn, name); + sd.chdir("/"); + SdFile file; + int ret = file.open(fn, O_CREAT | O_WRITE ); + if(!ret) { + return; + } + file.write(data); + file.close(); +} + +bool read_from_file(const char *name, char *data) { + if (!os.status.has_sd) return false; + + char *fn = tmp_buffer+TMP_BUFFER_SIZE-12; + strcpy_P(fn, name); + sd.chdir("/"); + SdFile file; + int ret = file.open(fn, O_READ ); + if(!ret) { + data[0]=0; + return true; // return true but with empty string + } + ret = file.fgets(data, TMP_BUFFER_SIZE); + data[TMP_BUFFER_SIZE]=0; + file.close(); + return true; +} + +#else // RPI/BBB/LINUX -#else void nvm_read_block(void *dst, const void *src, int len) { FILE *fp = fopen(NVM_FILENAME, "rb"); if(fp) { @@ -78,6 +116,40 @@ void nvm_write_byte(const byte *p, byte v) { } } +void write_to_file(const char *name, const char *data) { + FILE *file; + file = fopen(tmp_buffer, "wb"); + + if (!file) { return; } + + fwrite(data, 1, strlen(data), file); + fclose(file); +} + +bool read_from_file(const char *name, char *data) { + + FILE *file; + file = fopen(tmp_buffer, "rb"); + if(!file) { + data[0] = 0; + return true; + } + + int res; + if(fgets(tmp_buffer, TMP_BUFFER_SIZE, file)) { + res = strlen(tmp_buffer); + } else { + res = 0; + } + if (res <= 0) { + data[0] = 0; + } + + data[TMP_BUFFER_SIZE]=0; + fclose(file); + return true; +} + #if defined(OSPI) unsigned int detect_rpi_rev() { FILE * filp; @@ -107,7 +179,6 @@ unsigned int detect_rpi_rev() { // compare a string to nvm byte strcmp_to_nvm(const char* src, int _addr) { - byte i=0; byte c1, c2; byte *addr = (byte*)_addr; while(1) { diff --git a/utils.h b/utils.h index 24709df1..f0c191bb 100644 --- a/utils.h +++ b/utils.h @@ -37,7 +37,8 @@ uint16_t water_time_decode(byte i); ulong water_time_resolve(uint16_t v); byte water_time_encode_signed(int16_t i); int16_t water_time_decode_signed(byte i); - +void write_to_file(const char *name, const char *data); +bool read_from_file(const char *name, char *data); #if defined(ARDUINO) #define nvm_read_block eeprom_read_block #define nvm_write_block eeprom_write_block From 8afdebeb3be94692a948cf642eed7b30d55bf5f6 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Jun 2015 17:34:39 -0400 Subject: [PATCH 05/16] fix wto return variable --- DS1307RTC.cpp | 2 -- EtherCard.h | 2 ++ server.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DS1307RTC.cpp b/DS1307RTC.cpp index 9a729147..33bbf52b 100644 --- a/DS1307RTC.cpp +++ b/DS1307RTC.cpp @@ -97,8 +97,6 @@ void DS1307RTC::read( tmElements_t &tm) void DS1307RTC::write(tmElements_t &tm) { - static uint8_t initialized = 0; - if (ctrl_id == DS1307_CTRL_ID) { Wire.beginTransmission(ctrl_id); Wire.write((uint8_t)0x00); // reset register pointer diff --git a/EtherCard.h b/EtherCard.h index d1d3e5e7..5a3cca8c 100644 --- a/EtherCard.h +++ b/EtherCard.h @@ -18,7 +18,9 @@ // SI - Pin 11 // CS - Pin 8 // +#ifndef __PROG_TYPES_COMPAT__ #define __PROG_TYPES_COMPAT__ +#endif #ifndef EtherCard_h #define EtherCard_h diff --git a/server.cpp b/server.cpp index a3c4ff92..be8e867c 100644 --- a/server.cpp +++ b/server.cpp @@ -775,7 +775,7 @@ void server_json_controller_main() { } if(read_from_file(PSTR(WEATHER_OPTS_FILENAME), tmp_buffer)) { - bfill.emit_p(PSTR(",\"wto\":[$S]"), tmp_buffer); + bfill.emit_p(PSTR(",\"wto\":\"{$S}\""), tmp_buffer); } bfill.emit_p(PSTR("}")); delay(1); @@ -1384,7 +1384,7 @@ void analyze_get_url(char *p) } } - if((i==sizeof(urls)/sizeof(URLStruct))) { + if(i==sizeof(urls)/sizeof(URLStruct)) { // no server funtion found print_json_header(); bfill.emit_p(PSTR("{\"result\":$D}"), HTML_PAGE_NOT_FOUND); From ddcda8459e6b60054f574faa76e9441dac6e1201 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Jun 2015 17:37:33 -0400 Subject: [PATCH 06/16] fix wto return variable, fix ospi two file name --- server.cpp | 2 +- utils.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server.cpp b/server.cpp index be8e867c..6f01f0b6 100644 --- a/server.cpp +++ b/server.cpp @@ -775,7 +775,7 @@ void server_json_controller_main() { } if(read_from_file(PSTR(WEATHER_OPTS_FILENAME), tmp_buffer)) { - bfill.emit_p(PSTR(",\"wto\":\"{$S}\""), tmp_buffer); + bfill.emit_p(PSTR(",\"wto\":\"$S\""), tmp_buffer); } bfill.emit_p(PSTR("}")); delay(1); diff --git a/utils.cpp b/utils.cpp index fe2080a4..8103735b 100644 --- a/utils.cpp +++ b/utils.cpp @@ -118,7 +118,7 @@ void nvm_write_byte(const byte *p, byte v) { void write_to_file(const char *name, const char *data) { FILE *file; - file = fopen(tmp_buffer, "wb"); + file = fopen(name, "wb"); if (!file) { return; } @@ -129,7 +129,7 @@ void write_to_file(const char *name, const char *data) { bool read_from_file(const char *name, char *data) { FILE *file; - file = fopen(tmp_buffer, "rb"); + file = fopen(name, "rb"); if(!file) { data[0] = 0; return true; From 905dd02f8dd64c40217e52eb2be7ea40dad3e1c4 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Jun 2015 18:19:23 -0400 Subject: [PATCH 07/16] add O_TRUNC flag --- server.cpp | 10 ++-------- utils.cpp | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/server.cpp b/server.cpp index 6f01f0b6..72a32a8b 100644 --- a/server.cpp +++ b/server.cpp @@ -685,7 +685,7 @@ byte server_json_programs(char *p) print_json_header_with_bracket(); bfill.emit_p(PSTR("\"nprogs\":$D,\"nboards\":$D,\"mnp\":$D,\"mnst\":$D,\"pnsize\":$D,\"pd\":["), pd.nprograms, os.nboards, MAX_NUMBER_PROGRAMS, MAX_NUM_STARTTIMES, PROGRAM_NAME_SIZE); - byte pid, bid, i; + byte pid, i; ProgramStruct prog; for(pid=0;pid\n\n\n$F\n\n\n" ; +extern char wtopts_name[]; + #if defined(ARDUINO) void print_html_standard_header() { bfill.emit_p(PSTR("$F$F$F$F\r\n"), html200OK, htmlContentHTML, htmlNoCache, htmlAccessControl); @@ -774,7 +776,7 @@ void server_json_controller_main() { } } - if(read_from_file(PSTR(WEATHER_OPTS_FILENAME), tmp_buffer)) { + if(read_from_file(wtopts_name, tmp_buffer)) { bfill.emit_p(PSTR(",\"wto\":{$S}"), tmp_buffer); } bfill.emit_p(PSTR("}")); @@ -975,7 +977,7 @@ byte server_change_options(char *p) urlDecode(tmp_buffer); tmp_buffer[TMP_BUFFER_SIZE]=0; // store weather key - write_to_file(PSTR(WEATHER_OPTS_FILENAME), tmp_buffer); + write_to_file(wtopts_name, tmp_buffer); } if (err) { return HTML_DATA_OUTOFBOUND; diff --git a/utils.cpp b/utils.cpp index 2431e10f..73b150df 100644 --- a/utils.cpp +++ b/utils.cpp @@ -64,6 +64,17 @@ bool read_from_file(const char *name, char *data, int maxsize) { return true; } +void remove_file(const char *name) { + if (!os.status.has_sd) return; + + char *fn = tmp_buffer+TMP_BUFFER_SIZE-12; + strcpy_P(fn, name); + sd.chdir("/"); + DEBUG_PRINTLN(fn); + if (!sd.exists(fn)) return; + sd.remove(fn); +} + #else // RPI/BBB/LINUX void nvm_read_block(void *dst, const void *src, int len) { @@ -150,6 +161,10 @@ bool read_from_file(const char *name, char *data, int maxsize) { return true; } +void remove_file(const char *name) { + remove(name); +} + #if defined(OSPI) unsigned int detect_rpi_rev() { FILE * filp; diff --git a/utils.h b/utils.h index 23198bdc..a08f553d 100644 --- a/utils.h +++ b/utils.h @@ -39,6 +39,7 @@ byte water_time_encode_signed(int16_t i); int16_t water_time_decode_signed(byte i); void write_to_file(const char *name, const char *data); bool read_from_file(const char *name, char *data, int maxsize=TMP_BUFFER_SIZE); +void remove_file(const char *name); #if defined(ARDUINO) #define nvm_read_block eeprom_read_block #define nvm_write_block eeprom_write_block diff --git a/weather.cpp b/weather.cpp index c12b6aa8..675a37b1 100644 --- a/weather.cpp +++ b/weather.cpp @@ -31,6 +31,8 @@ extern char ether_buffer[]; #endif +extern char wtopts_name[]; + #include "OpenSprinkler.h" #include "utils.h" #include "server.h" @@ -106,7 +108,8 @@ void GetWeather() { //bfill=ether.tcpOffset(); char tmp[30]; - read_from_file(PSTR(WEATHER_OPTS_FILENAME), tmp, 30); + read_from_file(wtopts_name, tmp, 30); + DEBUG_PRINTLN(tmp); BufferFiller bf = (uint8_t*)tmp_buffer; bf.emit_p(PSTR("$D.py?loc=$E&key=$E&fwv=$D&wto=$S"), (int) os.options[OPTION_USE_WEATHER].value, @@ -198,7 +201,7 @@ void GetWeather() { BufferFiller bf = tmp_buffer; char tmp[100]; - read_from_file(PSTR(WEATHER_OPTS_FILENAME), 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, From 5edd47c220614b905390b907e6b144d01054f003 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 6 Jun 2015 02:40:21 -0400 Subject: [PATCH 10/16] add firmware version minor option, add support to display detect hardware type (ac, dc, or latch) on start-up --- OpenSprinkler.cpp | 42 +++++++++++++++++++++++------------------- defines.h | 3 +++ server.cpp | 2 +- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/OpenSprinkler.cpp b/OpenSprinkler.cpp index e9be34ca..cb769edb 100644 --- a/OpenSprinkler.cpp +++ b/OpenSprinkler.cpp @@ -107,6 +107,7 @@ static prog_char _json_log [] PROGMEM = "lg"; static prog_char _json_mas2[] PROGMEM = "mas2"; static prog_char _json_mton2[]PROGMEM = "mton2"; static prog_char _json_mtof2[]PROGMEM = "mtof2"; +static prog_char _json_fwm[] PROGMEM = "fwm"; static prog_char _json_reset[] PROGMEM = "reset"; /** Option names */ @@ -150,6 +151,7 @@ static prog_char _str_log [] PROGMEM = "Log?"; static prog_char _str_mas2[] PROGMEM = "Mas2:"; static prog_char _str_mton2[]PROGMEM = "Mas2 on adj:"; static prog_char _str_mtof2[]PROGMEM = "Mas2 off adj:"; +static prog_char _str_fwm[] PROGMEM = "FWm:"; static prog_char _str_reset[] PROGMEM = "Reset all?"; OptionStruct OpenSprinkler::options[NUM_OPTIONS] = { @@ -198,6 +200,7 @@ OptionStruct OpenSprinkler::options[NUM_OPTIONS] = { {0, MAX_NUM_STATIONS, _str_mas2, _json_mas2}, // index of master 2. 0: no master2 station {0, 60, _str_mton2,_json_mton2}, {60, 120, _str_mtof2,_json_mtof2}, + {OS_FW_MINOR, 0, _str_fwm, _json_fwm}, // firmware version {0, 1, _str_reset,_json_reset} }; @@ -894,24 +897,25 @@ void OpenSprinkler::options_setup() { lcd_set_brightness(); lcd_set_contrast(); - lcd.setCursor(2, 1); - lcd_print_pgm(PSTR("HW v")); - byte hwv = options[OPTION_HW_VERSION].value; - lcd.print((char)('0'+(hwv/10))); - lcd.print('.'); - lcd.print((char)('0'+(hwv%10))); - switch(hw_type) { - case HW_TYPE_DC: - lcd_print_pgm(PSTR(" DC")); - break; - case HW_TYPE_LATCH: - lcd_print_pgm(PSTR(" LA")); - break; - default: - lcd_print_pgm(PSTR(" AC")); + if (!button) { + lcd.setCursor(2, 1); + lcd_print_pgm(PSTR("HW v")); + byte hwv = options[OPTION_HW_VERSION].value; + lcd.print((char)('0'+(hwv/10))); + lcd.print('.'); + lcd.print((char)('0'+(hwv%10))); + switch(hw_type) { + case HW_TYPE_DC: + lcd_print_pgm(PSTR(" DC")); + break; + case HW_TYPE_LATCH: + lcd_print_pgm(PSTR(" LA")); + break; + default: + lcd_print_pgm(PSTR(" AC")); + } + delay(1000); } - delay(1000); - #endif } @@ -1224,13 +1228,13 @@ void OpenSprinkler::ui_set_options(int oid) switch (button & BUTTON_MASK) { case BUTTON_1: - if (i==OPTION_FW_VERSION || i==OPTION_HW_VERSION || + if (i==OPTION_FW_VERSION || i==OPTION_HW_VERSION || i==OPTION_FW_MINOR || i==OPTION_HTTPPORT_0 || i==OPTION_HTTPPORT_1) break; // ignore non-editable options if (options[i].max != options[i].value) options[i].value ++; break; case BUTTON_2: - if (i==OPTION_FW_VERSION || i==OPTION_HW_VERSION || + if (i==OPTION_FW_VERSION || i==OPTION_HW_VERSION || i==OPTION_FW_MINOR || i==OPTION_HTTPPORT_0 || i==OPTION_HTTPPORT_1) break; // ignore non-editable options if (options[i].value != 0) options[i].value --; break; diff --git a/defines.h b/defines.h index fc787a9c..0c024f5d 100644 --- a/defines.h +++ b/defines.h @@ -29,6 +29,8 @@ // if this number is different from the one stored in non-volatile memory // a device reset will be automatically triggered +#define OS_FW_MINOR 0 // Firmware minor version + /** Hardware version base numbers */ #define OS_HW_VERSION_BASE 0x00 #define OSPI_HW_VERSION_BASE 0x40 @@ -154,6 +156,7 @@ typedef enum { OPTION_MASTER_STATION_2, OPTION_MASTER_ON_ADJ_2, OPTION_MASTER_OFF_ADJ_2, + OPTION_FW_MINOR, OPTION_RESET, NUM_OPTIONS // total number of options } OS_OPTION_t; diff --git a/server.cpp b/server.cpp index fe931a10..e69371ce 100644 --- a/server.cpp +++ b/server.cpp @@ -911,7 +911,7 @@ byte server_change_options(char *p) // skip options that cannot be set through web UI if (oid==OPTION_RESET || oid==OPTION_DEVICE_ENABLE || oid==OPTION_FW_VERSION || oid==OPTION_HW_VERSION || - oid==OPTION_SEQUENTIAL_RETIRED) + oid==OPTION_FW_MINOR || oid==OPTION_SEQUENTIAL_RETIRED) continue; prev_value = os.options[oid].value; if (os.options[oid].max==1) os.options[oid].value = 0; // set a bool variable to 0 first From 53c088a3e6a154b84f71e699e627ebd9f859d589 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 6 Jun 2015 12:35:43 -0400 Subject: [PATCH 11/16] fix OSPi compile errors --- OpenSprinkler.cpp | 11 ++++++++--- server.cpp | 2 +- weather.cpp | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/OpenSprinkler.cpp b/OpenSprinkler.cpp index cb769edb..ea654364 100644 --- a/OpenSprinkler.cpp +++ b/OpenSprinkler.cpp @@ -21,7 +21,6 @@ * . */ -#include #include "OpenSprinkler.h" #include "gpio.h" @@ -53,11 +52,17 @@ ulong OpenSprinkler::external_ip; byte OpenSprinkler::water_percent_avg; char tmp_buffer[TMP_BUFFER_SIZE+1]; // scratch buffer -prog_char wtopts_name [] PROGMEM = WEATHER_OPTS_FILENAME; -extern SdFat sd; + +#if defined(ARDUINO) + prog_char wtopts_name[] PROGMEM = WEATHER_OPTS_FILENAME; +#else + char wtopts_name[] = WEATHER_OPTS_FILENAME; +#endif #if defined(ARDUINO) LiquidCrystal OpenSprinkler::lcd; + #include + extern SdFat sd; #elif defined(OSPI) // todo: LCD define for OSPi #endif diff --git a/server.cpp b/server.cpp index e69371ce..7cd0a3ef 100644 --- a/server.cpp +++ b/server.cpp @@ -109,7 +109,7 @@ static prog_uchar htmlReturnHome[] PROGMEM = "\n" ; -extern char wtopts_name[]; +extern const char wtopts_name[]; #if defined(ARDUINO) void print_html_standard_header() { diff --git a/weather.cpp b/weather.cpp index 675a37b1..161290b3 100644 --- a/weather.cpp +++ b/weather.cpp @@ -31,7 +31,7 @@ extern char ether_buffer[]; #endif -extern char wtopts_name[]; +extern const char wtopts_name[]; #include "OpenSprinkler.h" #include "utils.h" From 17a946df3a05a066eeccb3b4b1c1451fffd73bf2 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 6 Jun 2015 13:19:54 -0400 Subject: [PATCH 12/16] fix a bug that causes master station to not ignore rain when associated station ignores rain --- main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index eaaaa1e9..5aaccd5d 100644 --- a/main.cpp +++ b/main.cpp @@ -652,10 +652,13 @@ void process_dynamic_events(ulong curr_time) { sbits = os.station_bits[bid]; for(s=0;s<8;s++) { sid=bid*8+s; + + // ignore master stations because they are handled separately + if (os.status.mas == sid+1) continue; + if (os.status.mas2== sid+1) continue; // If this is a normal program (not a run-once or test program) // and either the controller is disabled, or // if raining and ignore rain bit is cleared - //if (!mm && (pd.scheduled_program_index[sid] != 254) && if ((pd.scheduled_program_index[sid]<99) && (!en || (rain && !(rbits&(1< Date: Sat, 6 Jun 2015 14:14:10 -0400 Subject: [PATCH 13/16] add support to turn off backlight for i2c LCD; add support to print out hardware type to /jo --- OpenSprinkler.cpp | 45 +++++++++++++++++++++++++++++---------------- OpenSprinkler.h | 2 +- defines.h | 4 ++-- main.cpp | 4 ++-- server.cpp | 4 ++-- 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/OpenSprinkler.cpp b/OpenSprinkler.cpp index ea654364..89935ba1 100644 --- a/OpenSprinkler.cpp +++ b/OpenSprinkler.cpp @@ -88,7 +88,6 @@ static prog_char _json_hp0 [] PROGMEM = "hp0"; static prog_char _json_hp1 [] PROGMEM = "hp1"; static prog_char _json_hwv [] PROGMEM = "hwv"; static prog_char _json_ext [] PROGMEM = "ext"; -static prog_char _json_seq [] PROGMEM = "_"; // the option 'sequential' is now retired static prog_char _json_sdt [] PROGMEM = "sdt"; static prog_char _json_mas [] PROGMEM = "mas"; static prog_char _json_mton[] PROGMEM = "mton"; @@ -102,7 +101,6 @@ static prog_char _json_devid[]PROGMEM = "devid"; static prog_char _json_con [] PROGMEM = "con"; static prog_char _json_lit [] PROGMEM = "lit"; static prog_char _json_dim [] PROGMEM = "dim"; -static prog_char _json_rlp [] PROGMEM = "_"; static prog_char _json_uwt [] PROGMEM = "uwt"; static prog_char _json_ntp1[] PROGMEM = "ntp1"; static prog_char _json_ntp2[] PROGMEM = "ntp2"; @@ -132,7 +130,6 @@ static prog_char _str_hp0 [] PROGMEM = "Port:"; static prog_char _str_hp1 [] PROGMEM = ""; static prog_char _str_hwv [] PROGMEM = "HW: "; static prog_char _str_ext [] PROGMEM = "Exp. board:"; -static prog_char _str_seq [] PROGMEM = ""; // the option 'sequential' is now retired static prog_char _str_sdt [] PROGMEM = "Stn delay:"; static prog_char _str_mas [] PROGMEM = "Mas1:"; static prog_char _str_mton[] PROGMEM = "Mas1 on adj:"; @@ -146,7 +143,6 @@ static prog_char _str_devid[]PROGMEM = "Dev. ID:"; static prog_char _str_con [] PROGMEM = "LCD con.:"; static prog_char _str_lit [] PROGMEM = "LCD lit.:"; static prog_char _str_dim [] PROGMEM = "LCD dim.:"; -static prog_char _str_rlp [] PROGMEM = ""; // the option 'relay pulsing' is now retired static prog_char _str_uwt [] PROGMEM = "Use weather?"; static prog_char _str_ntp1[] PROGMEM = "NTP.ip1:"; static prog_char _str_ntp2[] PROGMEM = ".ip2:"; @@ -181,7 +177,7 @@ OptionStruct OpenSprinkler::options[NUM_OPTIONS] = { #endif {OS_HW_VERSION, 0, _str_hwv, _json_hwv}, {0, MAX_EXT_BOARDS, _str_ext, _json_ext}, // number of extension board. 0: no extension boards - {1, 1, _str_seq, _json_seq}, // the option 'sequential' is now retired + {1, 1, 0, 0}, // the option 'sequential' is now retired {128, 247, _str_sdt, _json_sdt}, // station delay time (-59 minutes to 59 minutes). {0, MAX_NUM_STATIONS, _str_mas, _json_mas}, // index of master station. 0: no master station {0, 60, _str_mton, _json_mton}, // master on time [0,60] seconds @@ -194,8 +190,8 @@ OptionStruct OpenSprinkler::options[NUM_OPTIONS] = { {0, 255, _str_devid,_json_devid}, // device id {110, 255, _str_con, _json_con}, // lcd contrast {100, 255, _str_lit, _json_lit}, // lcd backlight - {15, 255, _str_dim, _json_dim}, // lcd dimming - {0, 200, _str_rlp, _json_rlp}, // the options 'relay pulse' is now retired + {15, 255, _str_dim, _json_dim}, // lcd dimming + {0, 200, 0, 0}, // the options 'relay pulse' is now retired {0, 255, _str_uwt, _json_uwt}, {50, 255, _str_ntp1, _json_ntp1}, // this and the next three bytes define the ntp server ip {97, 255, _str_ntp2, _json_ntp2}, @@ -286,7 +282,7 @@ byte OpenSprinkler::start_network() { if (options[OPTION_USE_DHCP].value) { // set up DHCP - // register with domain name "OpenSprinkler-xx" where xx is the last byte of the MAC address + // register with domain name "OS-xx" where xx is the last byte of the MAC address if (!ether.dhcpSetup()) return 0; // once we have valid DHCP IP, we write these into static IP / gateway IP byte *ip = ether.myip; @@ -362,13 +358,15 @@ void OpenSprinkler::lcd_start() { lcd.begin(); if (lcd.type() == LCD_STD) { + // this is standard 16x2 LCD // set PWM frequency for adjustable LCD backlight and contrast TCCR1B = 0x01; // turn on LCD backlight and contrast - pinMode(PIN_LCD_BACKLIGHT, OUTPUT); - pinMode(PIN_LCD_CONTRAST, OUTPUT); lcd_set_brightness(); lcd_set_contrast(); + } else { + // this is I2C LCD + // handle brightness } } #endif @@ -897,8 +895,6 @@ void OpenSprinkler::options_setup() { } // turn on LCD backlight and contrast - pinMode(PIN_LCD_BACKLIGHT, OUTPUT); - pinMode(PIN_LCD_CONTRAST, OUTPUT); lcd_set_brightness(); lcd_set_contrast(); @@ -1265,7 +1261,7 @@ void OpenSprinkler::ui_set_options(int oid) else { i = (i+1) % NUM_OPTIONS; } - if(pgm_read_byte(options[i].json_str)=='_') i++; + if(options[i].json_str==0) i++; } break; } @@ -1278,11 +1274,28 @@ void OpenSprinkler::ui_set_options(int oid) } void OpenSprinkler::lcd_set_contrast() { - analogWrite(PIN_LCD_CONTRAST, options[OPTION_LCD_CONTRAST].value); + // set contrast is only valid for standard LCD + if (lcd.type()==LCD_STD) { + pinMode(PIN_LCD_CONTRAST, OUTPUT); + analogWrite(PIN_LCD_CONTRAST, options[OPTION_LCD_CONTRAST].value); + } } -void OpenSprinkler::lcd_set_brightness() { - analogWrite(PIN_LCD_BACKLIGHT, 255-options[OPTION_LCD_BACKLIGHT].value); +void OpenSprinkler::lcd_set_brightness(byte value) { + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + if (lcd.type()==LCD_I2C) { + if (value) lcd.backlight(); + else lcd.noBacklight(); + } + #endif + if (lcd.type()==LCD_STD) { + pinMode(PIN_LCD_BACKLIGHT, OUTPUT); + if (value) { + analogWrite(PIN_LCD_BACKLIGHT, 255-options[OPTION_LCD_BACKLIGHT].value); + } else { + analogWrite(PIN_LCD_BACKLIGHT, 255-options[OPTION_LCD_DIMMING].value); + } + } } #endif // end of LCD and button functions diff --git a/OpenSprinkler.h b/OpenSprinkler.h index 9de6b240..d026e7f8 100644 --- a/OpenSprinkler.h +++ b/OpenSprinkler.h @@ -178,7 +178,7 @@ class OpenSprinkler { // -- UI functions -- static void ui_set_options(int oid); // ui for setting options (oid-> starting option index) - static void lcd_set_brightness(); + static void lcd_set_brightness(byte value=1); static void lcd_set_contrast(); private: static void lcd_print_option(int i); // print an option to the lcd diff --git a/defines.h b/defines.h index 0c024f5d..46535521 100644 --- a/defines.h +++ b/defines.h @@ -51,7 +51,7 @@ /** Non-volatile memory (NVM) defines */ #if defined(ARDUINO) -/** NVM data structure: +/** 2KB NVM data structure: * | | | |---STRING PARAMETERS---| |----STATION ATTRIBUTES----- | | * | UID | PROGRAM_DATA | CON | PWD | LOC | URL | KEY | STATION_NAMES | MAS | IGR | MAS2 | DIS | SEQ | OPTIONS | * | (8) | (996) | (8) |(32) |(48) |(64) |(32) | (6*8*16)=768 | (6) | (6) | (6) | (6) | (6) | (62) | @@ -146,7 +146,7 @@ typedef enum { OPTION_LCD_CONTRAST, OPTION_LCD_BACKLIGHT, OPTION_LCD_DIMMING, - OPTION_RELAY_PULSE, + OPTION_RELAY_PULSE_RETIRED, OPTION_USE_WEATHER, OPTION_NTP_IP1, OPTION_NTP_IP2, diff --git a/main.cpp b/main.cpp index 5aaccd5d..a5239473 100644 --- a/main.cpp +++ b/main.cpp @@ -82,7 +82,7 @@ static byte ui_state_runprog = 0; void ui_state_machine() { if (!os.button_timeout) { - analogWrite(PIN_LCD_BACKLIGHT, 255-os.options[OPTION_LCD_DIMMING].value); + os.lcd_set_brightness(0); ui_state = UI_STATE_DEFAULT; // also recover to default state } @@ -91,7 +91,7 @@ void ui_state_machine() { if (button & BUTTON_FLAG_DOWN) { // repond only to button down events os.button_timeout = LCD_BACKLIGHT_TIMEOUT; - analogWrite(PIN_LCD_BACKLIGHT, 255-os.options[OPTION_LCD_BACKLIGHT].value); // button is pressed, turn on LCD right away + os.lcd_set_brightness(1); } else { return; } diff --git a/server.cpp b/server.cpp index 7cd0a3ef..ac0e18cc 100644 --- a/server.cpp +++ b/server.cpp @@ -661,7 +661,7 @@ void server_json_options_main() { int32_t v=os.options[oid].value; if (oid==OPTION_MASTER_OFF_ADJ || oid==OPTION_MASTER_OFF_ADJ_2) {v-=60;} if (oid==OPTION_STATION_DELAY_TIME) {v=water_time_decode_signed(v);} - if (pgm_read_byte(os.options[oid].json_str)=='_') continue; + if (os.options[oid].json_str==0) continue; if (oid==OPTION_DEVICE_ID && os.status.has_hwmac) continue; // do not send DEVICE ID if hardware MAC exists bfill.emit_p(PSTR("\"$F\":$D"), os.options[oid].json_str, v); @@ -669,7 +669,7 @@ void server_json_options_main() { bfill.emit_p(PSTR(",")); } - bfill.emit_p(PSTR(",\"dexp\":$D,\"mexp\":$D}"), os.detect_exp(), MAX_EXT_BOARDS); + bfill.emit_p(PSTR(",\"dexp\":$D,\"mexp\":$D,\"hwt\":$D}"), os.detect_exp(), MAX_EXT_BOARDS, os.hw_type); } From cdc48508c73fcc036ee1caf949ee23aa09a57b25 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 6 Jun 2015 19:07:35 -0400 Subject: [PATCH 14/16] change apply_all_stations to support boost converter; add boost time option --- OpenSprinkler.cpp | 162 ++++++++++++++++++++++++++++++---------------- defines.h | 2 +- dhcp.cpp | 4 +- main.cpp | 3 +- server.cpp | 9 +++ weather.cpp | 2 - 6 files changed, 120 insertions(+), 62 deletions(-) diff --git a/OpenSprinkler.cpp b/OpenSprinkler.cpp index 89935ba1..ba01516f 100644 --- a/OpenSprinkler.cpp +++ b/OpenSprinkler.cpp @@ -101,6 +101,7 @@ static prog_char _json_devid[]PROGMEM = "devid"; static prog_char _json_con [] PROGMEM = "con"; static prog_char _json_lit [] PROGMEM = "lit"; static prog_char _json_dim [] PROGMEM = "dim"; +static prog_char _json_bst [] PROGMEM = "bst"; static prog_char _json_uwt [] PROGMEM = "uwt"; static prog_char _json_ntp1[] PROGMEM = "ntp1"; static prog_char _json_ntp2[] PROGMEM = "ntp2"; @@ -143,6 +144,7 @@ static prog_char _str_devid[]PROGMEM = "Dev. ID:"; static prog_char _str_con [] PROGMEM = "LCD con.:"; static prog_char _str_lit [] PROGMEM = "LCD lit.:"; static prog_char _str_dim [] PROGMEM = "LCD dim.:"; +static prog_char _str_bst [] PROGMEM = "Boost:"; static prog_char _str_uwt [] PROGMEM = "Use weather?"; static prog_char _str_ntp1[] PROGMEM = "NTP.ip1:"; static prog_char _str_ntp2[] PROGMEM = ".ip2:"; @@ -190,9 +192,9 @@ OptionStruct OpenSprinkler::options[NUM_OPTIONS] = { {0, 255, _str_devid,_json_devid}, // device id {110, 255, _str_con, _json_con}, // lcd contrast {100, 255, _str_lit, _json_lit}, // lcd backlight - {15, 255, _str_dim, _json_dim}, // lcd dimming - {0, 200, 0, 0}, // the options 'relay pulse' is now retired - {0, 255, _str_uwt, _json_uwt}, + {15, 255, _str_dim, _json_dim}, // lcd dimming + {60, 250, _str_bst, _json_bst}, // boost time (only valid to DC and LATCH type) + {0, 255, _str_uwt, _json_uwt}, // weather algorithm (0 means not using weather algorithm) {50, 255, _str_ntp1, _json_ntp1}, // this and the next three bytes define the ntp server ip {97, 255, _str_ntp2, _json_ntp2}, {210, 255, _str_ntp3, _json_ntp3}, @@ -431,11 +433,11 @@ void OpenSprinkler::begin() { /*pinMode(PIN_RELAY, OUTPUT); digitalWrite(PIN_RELAY, LOW);*/ + hw_type = HW_TYPE_AC; #if defined(ARDUINO) // AVR SD and LCD functions // Init I2C Wire.begin(); - hw_type = HW_TYPE_AC; #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) // OS 2.3 specific detections uint8_t ret; @@ -464,57 +466,57 @@ void OpenSprinkler::begin() { #endif lcd_start(); - lcd_print_pgm(PSTR(" OpenSprinkler")); - + // define lcd custom icons - byte lcd_wifi_char[8] = { - B00000, - B10100, - B01000, - B10101, - B00001, - B00101, - B00101, - B10101 - }; - byte lcd_sd_char[8] = { - B00000, - B00000, - B11111, - B10001, - B11111, - B10001, - B10011, - B11110 - }; - byte lcd_rain_char[8] = { - B00000, - B00000, - B00110, - B01001, - B11111, - B00000, - B10101, - B10101 - }; - byte lcd_connect_char[8] = { - B00000, - B00000, - B00111, - B00011, - B00101, - B01000, - B10000, - B00000 - }; - lcd.createChar(1, lcd_wifi_char); - lcd_wifi_char[1]=0; - lcd_wifi_char[2]=0; - lcd_wifi_char[3]=1; - lcd.createChar(0, lcd_wifi_char); - lcd.createChar(2, lcd_sd_char); - lcd.createChar(3, lcd_rain_char); - lcd.createChar(4, lcd_connect_char); + byte _icon[8]; + // WiFi icon + _icon[0] = B00000; + _icon[1] = B10100; + _icon[2] = B01000; + _icon[3] = B10101; + _icon[4] = B00001; + _icon[5] = B00101; + _icon[6] = B00101; + _icon[7] = B10101; + lcd.createChar(1, _icon); + + _icon[1]=0; + _icon[2]=0; + _icon[3]=1; + lcd.createChar(0, _icon); + + // uSD card icon + _icon[0] = B00000; + _icon[1] = B00000; + _icon[2] = B11111; + _icon[3] = B10001; + _icon[4] = B11111; + _icon[5] = B10001; + _icon[6] = B10011; + _icon[7] = B11110; + lcd.createChar(2, _icon); + + // Rain icon + _icon[0] = B00000; + _icon[1] = B00000; + _icon[2] = B00110; + _icon[3] = B01001; + _icon[4] = B11111; + _icon[5] = B00000; + _icon[6] = B10101; + _icon[7] = B10101; + lcd.createChar(3, _icon); + + // Connect icon + _icon[0] = B00000; + _icon[1] = B00000; + _icon[2] = B00111; + _icon[3] = B00011; + _icon[4] = B00101; + _icon[5] = B01000; + _icon[6] = B10000; + _icon[7] = B00000; + lcd.createChar(4, _icon); // set sd cs pin high to release SD pinMode(PIN_SD_CS, OUTPUT); @@ -546,7 +548,13 @@ void OpenSprinkler::apply_all_station_bits() { digitalWrite(PIN_SR_LATCH, LOW); byte bid, s, sbits; - + bool engage_booster = false; + + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + // old station bits + static byte old_station_bits[MAX_EXT_BOARDS+1]; + #endif + // Shift out all station bit values // from the highest bit to the lowest for(bid=0;bid<=MAX_EXT_BOARDS;bid++) { @@ -554,6 +562,17 @@ void OpenSprinkler::apply_all_station_bits() { sbits = station_bits[MAX_EXT_BOARDS-bid]; else sbits = 0; + + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + // check if any station is changing from 0 to 1 + // take the bit inverse of the old status + // and with the new status + if ((~old_station_bits[MAX_EXT_BOARDS-bid]) & sbits) { + engage_booster = true; + } + old_station_bits[MAX_EXT_BOARDS-bid] = sbits; + #endif + for(s=0;s<8;s++) { digitalWrite(PIN_SR_CLOCK, LOW); #if defined(OSPI) // if OSPi, use dynamically assigned pin_sr_data @@ -564,7 +583,26 @@ void OpenSprinkler::apply_all_station_bits() { digitalWrite(PIN_SR_CLOCK, HIGH); } } + + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + if((hw_type==HW_TYPE_DC) && engage_booster) { + DEBUG_PRINTLN(F("engage booster")); + // boost voltage + digitalWrite(PIN_BOOST, HIGH); + delay(250); + digitalWrite(PIN_BOOST, LOW); + + // enable boosted voltage for a short period of time + digitalWrite(PIN_BOOST_EN, HIGH); + digitalWrite(PIN_SR_LATCH, HIGH); + delay(500); + digitalWrite(PIN_BOOST_EN, LOW); + } else { + digitalWrite(PIN_SR_LATCH, HIGH); + } + #else digitalWrite(PIN_SR_LATCH, HIGH); + #endif } void OpenSprinkler::rainsensor_status() { @@ -899,6 +937,8 @@ void OpenSprinkler::options_setup() { lcd_set_contrast(); if (!button) { + // flash screen + lcd_print_line_clear_pgm(PSTR(" OpenSprinkler"),0); lcd.setCursor(2, 1); lcd_print_pgm(PSTR("HW v")); byte hwv = options[OPTION_HW_VERSION].value; @@ -1153,6 +1193,18 @@ void OpenSprinkler::lcd_print_option(int i) { lcd_set_brightness(); lcd.print((int)options[i].value); break; + case OPTION_BOOST_TIME: + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + if(hw_type==HW_TYPE_AC) { + lcd.print('-'); + } else { + lcd.print((int)options[i].value*4); + lcd_print_pgm(PSTR(" ms")); + } + #else + lcd.print('-'); + #endif + break; default: // if this is a boolean option if (options[i].max==1) diff --git a/defines.h b/defines.h index 46535521..7971ff85 100644 --- a/defines.h +++ b/defines.h @@ -146,7 +146,7 @@ typedef enum { OPTION_LCD_CONTRAST, OPTION_LCD_BACKLIGHT, OPTION_LCD_DIMMING, - OPTION_RELAY_PULSE_RETIRED, + OPTION_BOOST_TIME, OPTION_USE_WEATHER, OPTION_NTP_IP1, OPTION_NTP_IP2, diff --git a/dhcp.cpp b/dhcp.cpp index 44644a25..cb8d73c7 100644 --- a/dhcp.cpp +++ b/dhcp.cpp @@ -283,8 +283,8 @@ bool EtherCard::dhcpSetup () { using_dhcp = true; // Set a unique hostname, use Arduino-?? with last octet of mac address - hostname[3] = '0' + (mymac[5] >> 4); - hostname[4] = '0' + (mymac[5] & 0x0F); + hostname[3] = 'A' + (mymac[5] >> 4); + hostname[4] = 'A' + (mymac[5] & 0x0F); dhcpState = DHCP_STATE_INIT; uint16_t start = millis(); diff --git a/main.cpp b/main.cpp index a5239473..926fd3ee 100644 --- a/main.cpp +++ b/main.cpp @@ -134,8 +134,7 @@ void ui_state_machine() { os.lcd_print_pgm(PSTR("(lswc)")); ui_state = UI_STATE_DISP_IP; } else { - //os.reboot_dev(); - os.status.safe_reboot = 1; + os.reboot_dev(); } } else { // clicking B2: display MAC and gate way IP os.lcd.clear(); diff --git a/server.cpp b/server.cpp index ac0e18cc..acf785dc 100644 --- a/server.cpp +++ b/server.cpp @@ -661,6 +661,14 @@ void server_json_options_main() { int32_t v=os.options[oid].value; if (oid==OPTION_MASTER_OFF_ADJ || oid==OPTION_MASTER_OFF_ADJ_2) {v-=60;} if (oid==OPTION_STATION_DELAY_TIME) {v=water_time_decode_signed(v);} + #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + if (oid==OPTION_BOOST_TIME) { + if (os.hw_type==HW_TYPE_AC) continue; + else v<<=2; + } + #else + if (oid==OPTION_BOOST_TIME) continue; + #endif if (os.options[oid].json_str==0) continue; if (oid==OPTION_DEVICE_ID && os.status.has_hwmac) continue; // do not send DEVICE ID if hardware MAC exists bfill.emit_p(PSTR("\"$F\":$D"), @@ -978,6 +986,7 @@ byte server_change_options(char *p) tmp_buffer[TMP_BUFFER_SIZE]=0; // store weather key write_to_file(wtopts_name, tmp_buffer); + weather_change = true; } if (err) { return HTML_DATA_OUTOFBOUND; diff --git a/weather.cpp b/weather.cpp index 161290b3..9f096d75 100644 --- a/weather.cpp +++ b/weather.cpp @@ -51,7 +51,6 @@ static void getweather_callback(byte status, uint16_t off, uint16_t len) { #else char *p = ether_buffer; #endif - /* scan the buffer until the first & symbol */ while(*p && *p!='&') { p++; @@ -109,7 +108,6 @@ void GetWeather() { //bfill=ether.tcpOffset(); char tmp[30]; read_from_file(wtopts_name, tmp, 30); - DEBUG_PRINTLN(tmp); BufferFiller bf = (uint8_t*)tmp_buffer; bf.emit_p(PSTR("$D.py?loc=$E&key=$E&fwv=$D&wto=$S"), (int) os.options[OPTION_USE_WEATHER].value, From adfb4fdbadbddaad37d573b4fa774d50ee4338ca Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 6 Jun 2015 23:06:44 -0400 Subject: [PATCH 15/16] fix bug in OSPi's read_from_file --- utils.cpp | 4 ++-- weather.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/utils.cpp b/utils.cpp index 73b150df..e5ed1767 100644 --- a/utils.cpp +++ b/utils.cpp @@ -147,8 +147,8 @@ bool read_from_file(const char *name, char *data, int maxsize) { } int res; - if(fgets(tmp_buffer, maxsize, file)) { - res = strlen(tmp_buffer); + if(fgets(data, maxsize, file)) { + res = strlen(data); } else { res = 0; } diff --git a/weather.cpp b/weather.cpp index 9f096d75..dce70f98 100644 --- a/weather.cpp +++ b/weather.cpp @@ -225,7 +225,7 @@ void GetWeather() { } }; *dst = *src; - + char urlBuffer[255]; strcpy(urlBuffer, "GET /weather"); strcat(urlBuffer, dst); From 12880aa2c115ce5ab35e6342a4658120547bb45f Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 6 Jun 2015 23:23:06 -0400 Subject: [PATCH 16/16] fix a bug in weather call where the ethernet buffer wasn't cleared correctly --- weather.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weather.cpp b/weather.cpp index dce70f98..9e38c26f 100644 --- a/weather.cpp +++ b/weather.cpp @@ -233,7 +233,7 @@ void GetWeather() { client.write((uint8_t *)urlBuffer, strlen(urlBuffer)); - bzero(tmp_buffer, TMP_BUFFER_SIZE); + bzero(ether_buffer, ETHER_BUFFER_SIZE); time_t timeout = os.now_tz() + 5; // 5 seconds timeout while(os.now_tz() < timeout) {