DS1307_Command.cpp 5.68 KB
/*
   DS1307_Command.cpp - CLI library for Arduino/ESP8266 - DS1307 Command implementation

   Version 1.0, latest version, documentation and bugtracker available at:
                https://gitlab.lindenaar.net/arduino/CLI

   Copyright (c) 2019 Frederik Lindenaar

   This library is free software: you can redistribute it and/or modify it under
   the terms of version 3 of the GNU General Public License as published by the
   Free Software Foundation, or (at your option) a later version of the license.

   This code is distributed in the hope that it will be useful but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along with
   this program.  If not, visit <http://www.gnu.org/licenses/> to download it.
*/

#include <TimeLib.h>
#include <DS1307RTC.h>

#include "DS1307_Command.h"

DS1307_Command::DS1307_Command(CLI &cli) :                              // Constructor for ds1307 command
  CLI_Command(cli,                                                      // CLI to register with
              PSTR("ds1307"),                                           // Command name
              PSTR("Set and/or show DS1307 RTC date and time"),         // Description
              PSTR("Usage:\tds1307 [YYYY-MM-DD] [HH:MM[:SS]]\n"         // Usage Help
                   "where YYYY-MM-DD is the date in ISO format\n"
                   "  and HH:MM[:SS] the new time (seconds are optional)\n")) { };

// macro to determine whether the given year is a leap year
#define LEAP_YEAR(y)    (!((y) % 4) && (((y) % 100 ) || !((y) % 400) ))

inline uint8_t monthDays(int y, uint8_t m) {    // get number of days for month
  return (m == 2) ? 28 + LEAP_YEAR(y) : 30 + ((m & 1) == (m <= 7));
}


// Function below parses the string passed in params (if provided) and sets the
// time accordingly. returns false in case an invalid date/time was found (and
// an invalid parameter message will be given) and true in case of no params or
// when a string of the format: [YYYY-MM-DD] [HH:MM[:SS]] is found. In case a
// partial date/time is provided, the remaining part will be substituted with
// the DS1307's current value. Additional whitespaces are ignored and values are
// checked for validity.
bool DS1307_Command::setparams(const char *params) {
  if (!params) return true;                     // return true when no parameter
  int value;
  tmElements_t tm;
  if (const char *sep = CLI_STR_parseInt(params, value)) { // parse first integer value
    RTC.read(tm);                               // Got a valid start get current time
    // check if character following the integer is a '-' and we have a valid year
    if (*sep == '-' && value >= tmYearToCalendar(0) && value <= tmYearToCalendar(255)) {
      // Got a valid year and date separator, parse the rest of the date
      tm.Year = CalendarYrToTm(value);
      if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 1 && value <= 12 && *sep == '-') {
        tm.Month = value;
        if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 1 &&
                        value <= monthDays(tmYearToCalendar(tm.Year), value)) {
          tm.Day = value;
          sep = CLI_STR_skipSep(sep);
          if (*sep) sep = CLI_STR_parseInt(sep, value); // parse the hour integer
        } else return false;    // exit if parsing a date and day is invald
      } else return false;      // exit if parsing a date and month is invald
    }
    // check if character following the integer is a ':' and we have a valid hour
    if (*sep == ':' && value >= 0 && value < 24) {
      // Got a valid hour and time separator, parse the rest of the time
      tm.Hour = value;
      if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 0 && value <= 59) {
        tm.Minute = value;
        if (*sep == ':') {      // if next char is a separator, parse seconds
          if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 0 && value <= 59) {
            tm.Second = value;
          } else return false;
        } else tm.Second = 0;   // otherwise set seconds to 0
      } else return false; // exit if minutes is invalid
    }
    if (*CLI_STR_skipSep(sep)) return false;     // if not the end of the string, return false
    RTC.write(tm);      // if we get here, we have a valid time in tm, set it
    return true;        // return true as we got valid input
  }
  return false;         // no valid parameter, return false
}

bool DS1307_Command::execute(CLI &cli) {    // execute  prints the current time
  tmElements_t tm;
  if (RTC.read(tm)) {                           // get current time from DS1307
    cli.print(dayStr(tm.Wday));                 // print day of week
    cli.print(' ');
    cli.print(tm.Day);                          // print day of month
    cli.print(' ');
    cli.print(monthStr(tm.Month));              // print month name
    cli.print(' ');
    cli.print(tmYearToCalendar(tm.Year));       // print year
    cli.print(' ');
    cli.print2digits(tm.Hour);                  // print hour
    cli.print(':');
    cli.print2digits(tm.Minute);                // print minute
    cli.print(':');
    cli.print2digits(tm.Second);                // print seconds
  } else {                              // if get time fails, print an error
    if (RTC.chipPresent()) {            // if a chip was found, it is not set
      cli.print_P(PSTR("Error: cannot read RTC clock, please set time\n"));
    } else {                            // otherwise tell user no DS1307 was found
      cli.print_P(PSTR("Error: RTC clock not found, check wiring\n"));
    }
  }
  return false;                 // We're done and should not be called again
}