/**
 * @file SDI12.h
 * @copyright Stroud Water Research Center
 * @license This library is published under the BSD-3 license.
 * @date August 2013
 * @author Kevin M.Smith <SDI12@ethosengineering.org>
 *
 * @brief This file contains the main class for the SDI-12 implementation.
 *
 * ========================== Arduino SDI-12 ==================================
 *
 * An Arduino library for SDI-12 communication with a wide variety of environmental
 * sensors. This library provides a general software solution, without requiring any
 * additional hardware.
 *
 * ======================== Attribution & License =============================
 *
 * Copyright (C) 2013  Stroud Water Research Center
 * Available at https://github.com/EnviroDIY/Arduino-SDI-12
 *
 * Authored initially in August 2013 by:
 *          Kevin M. Smith (http://ethosengineering.org)
 *          Inquiries: SDI12@ethosengineering.org
 *
 * Modified 2017 by Manuel Jimenez Buendia to work with ARM based processors (Arduino
 * Zero)
 *
 * Maintenance and merging 2017 by Sara Damiano
 *
 * based on the SoftwareSerial library (formerly NewSoftSerial), authored by:
 *         ladyada (http://ladyada.net)
 *         Mikal Hart (http://www.arduiniana.org)
 *         Paul Stoffregen (http://www.pjrc.com)
 *         Garrett Mace (http://www.macetech.com)
 *         Brett Hagman (http://www.roguerobotics.com/)
 *
 * This library is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this library; if not, write to the Free Software Foundation, Inc., 51 Franklin
 * Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
/**
 * @page specifications Notes on SDI-12, Specification v1.4
 *
 * @tableofcontents
 *
 * @section overview Overview
 *
 * SDI-12 is a communications protocol that uses a single data wire to communicate with
 * up to 62 uniquely addressed sensors.  So long as each sensor supports SDI-12, mixed
 * sensor types can appear on the same data bus.  Each address is a single character.
 * The valid ranges are 0-9, a-z, and A-Z. Only the data logger can initiate
 * communications on the data bus.
 *
 * It does so by pulling the data line into a 5v state for at least 12 milliseconds to
 * wake up all the sensors, before returning the line into a 0v state for 8 milliseconds
 * announce an outgoing command.  The command contains both the action to be taken, and
 * the address of the device who should respond.  If there is a sensor on the bus with
 * that address, it is responsible for responding to the command.  Sensors should ignore
 * commands that were not issued to them, and should return to a sleep state until the
 * data logger again issues the wakeup sequence.
 *
 * @section connection_details Connection Details
 *
 * **Physical Connections:**
 *   - 1 data line (0v - 5.5v)
 *   - 1 12v power line (9.6v - 16v)
 *   - 1 ground line
 *
 * **Baud Rate:**
 *   - 1200 bits per second
 *
 * **Data Frame Format:**
 *   - 10 bits per data frame
 *   - 1 start bit
 *   - 7 data bits (least significant bit first)
 *   - 1 even parity bit
 *   - 1 stop bit
 *
 * Data Line:  SDI-12 communication uses a single bi-directional data line with
 * three-state, inverse logic.
 *
 * | LINE CONDITION | BINARY STATE | VOLTAGE RANGE     |
 * |----------------|--------------|-------------------|
 * | marking        |      1       | -0.5 to 1.0 volts |
 * | spacing        |      0       | 3.5 to 5.5 volts  |
 * | transition     |  undefined   | 1.0 to 3.5 volts  |
 *
 * While a series of bits is being transmitted on the dataline, the voltage level on
 * that line might look something like this:
 *
 * @code
 *       _____       _____       _____       _____       _____     spacing
 * 5v   |     |     |     |     |     |     |     |     |     |
 *      |  0  |  1  |  0  |  1  |  0  |  1  |  0  |  1  |  0  | transition
 * Ov___|     |_____|     |_____|     |_____|     |_____|     |___ marking
 * @endcode
 *
 * @note Although the specification gives these voltages, some manufacturers chose to
 * implement SDI-12 at other logic levels - ie, with spacing voltages lower or higher
 * than the specified ~5V.
 *
 * For more information, and for a list of commands and responses, please see
 * SDI-12.org, official site of the SDI-12 Support Group.
 */
/*** ==================== Code Organization ======================
 * - Includes, Defines, & Variable Declarations
 * - Buffer Setup
 * - Reading from the SDI-12 Buffer
 * - Constructor, Destructor, Begins, and Setters
 * - Using more than one SDI-12 object, isActive() and setActive()
 * - Setting Proper Data Line States
 * - Waking up and Talking to the Sensors
 * - Interrupt Service Routine (getting the data into the buffer)
 */


#ifndef SRC_SDI12_H_
#define SRC_SDI12_H_

//  Import Required Libraries

#include <inttypes.h>      // integer types library
#include <Arduino.h>       // Arduino core library
#include <Stream.h>        // Arduino Stream library
#include "SDI12_boards.h"  //  Include timer information

/// Helper for strings stored in flash
typedef const __FlashStringHelper* FlashString;

/// a char not found in a valid ASCII numeric field
#define NO_IGNORE_CHAR '\x01'

/* SDI-12 Data Buffer Size Specification */
// The following data buffer sizes does not include CR+LF and CRC

/**
 * @brief The maximum number of characters in a single value in a data response.
 *
 * From SDI-12 Protocol v1.4, Table 11 The send data command (aD0!, aD1! . . . aD9!):
 * the value portion must be structred as pd.d
 * - p - the polarity sign (+ or -)
 * - d - numeric digits before the decimal place
 * - . - the decimal point (optional)
 * - d - numeric digits after the decimal point
 * - the maximum number of digits for a data value is 7, even without a decimal point
 * - the minimum number of digits for a data value (excluding the decimal point) is 1
 * - the maximum number of characters in a data value is 9 (the (polarity sign + 7
 * digits + the decimal point))
 * - The polarity symbol (+ or -) acts as a delimeter between the numeric values
 */
#define SDI12_VALUE_STR_SIZE 9
/**
 * @brief The maximum length of a standard data command response
 *
 * From SDI-12 Protocol v1.4, Section 4.4 SDI-12 Commands and Responses:
 * The maximum number of characters that can be returned in the <values> part of the
 * response to a D command is either 35 or 75. If the D command is issued to retrieve
 * data in response to a concurrent measurement command, or in response to a high-volume
 * ASCII measurement command, the maximum is 75. The maximum is also 75 in response to a
 * continuous measurement command. Otherwise, the maximum is 35.
 */
#define SDI12_DATA_STR_SIZE 35
/**
 * @brief The maximum length of a data response to a concurrent, continuous, or high
 * volume ASCII
 *
 * @see SDI12_VALUE_STR_SIZE
 */
#define SDI12_HV_STR_SIZE 75

#ifndef SDI12_BUFFER_SIZE
/**
 * @brief The buffer size for incoming SDI-12 data.
 *
 * All responses should be less than 81 characters:
 * - address is a single (1) character
 * - values has a maximum value of 75 characters
 * - CRC is 3 characters
 * - CR is a single character
 * - LF is a single character
 */
#define SDI12_BUFFER_SIZE 81
#endif

// SDI-12 Timing Specification
/**
 * @brief The size of a bit in microseconds
 *
 * 1200 baud = 1200 bits/second ~ 833.333 µs/bit
 */
#define SDI12_BIT_WIDTH_MICROS static_cast<uint16_t>(833)
/**
 * @brief The required "break" before sending commands, >= 12ms.  The line level is HIGH
 * for the break.
 */
#define SDI12_LINE_BREAK_MICROS static_cast<uint16_t>(12100)
/**
 * @brief The required mark before a command or response, >= 8.33ms.  The line level is
 * LOW for the marking.
 */
#define SDI12_LINE_MARK_MICROS static_cast<uint16_t>(8400)

/**
 * Possible SDI-12 States
 *
 * WAITING_FOR_BREAK:
 * - Sensor (slave) is asleep, waiting for the data recorder (master) to hold the line
 * high for >= 12ms.  Or the data recorder has not initiated communication with a sensor
 * in too long and needs to re-alert it.
 * - Starts:
 *   - After a sensor receives an invalid address (return to sleep)
 *   - When the master wants to address a different sensor
 *   - After line has been in marking (LOW) for > 100 ms (sensor returns to sleep)
 *   - After the line has been in marking (LOW) for > 87 ms  (sensor awaits break
 * without sleeping, recorder must send break)
 * - Ends:
 *    - After 12 ms break has finished
 *
 * WAITING_FOR_MARKING:
 * - Sensor has received a >= 12ms HIGH break and is waiting for the data recorder to
 * send >= 8.33 ms of LOW marking.
 * - Data recorder has finished sending a command, has relinquished the line, and is
 * waiting for the sensor to hold the line LOW for >= 8.33 ms of marking
 * - Starts:
 *   - After line has been held continuously HIGH for >= 12ms
 * - Ends:
 *   - After the line has been in marking (LOW) for > 87 ms
 *
 * WAITING_FOR_START_BIT:
 * - Line has been held low for >= 8.33 ms of marking
 * - Ends:
 *   - > 15 ms after the last stop bit of a command (for the recorder/master)
 *   - > 1.66 ms after the last stop bit between characters within a command or response
 *
 */

/**
 * @brief A mask for the #rxState while waiting for a start bit; 0b11111111
 */
#define WAITING_FOR_START_BIT 0xFF


#ifndef SDI12_IGNORE_PARITY
/**
 * @brief Check the value of the parity bit on reception
 */
#define SDI12_CHECK_PARITY
#endif

#ifndef SDI12_WAKE_DELAY
/**
 * @brief The amount of additional time in milliseconds that the sensor takes to wake
 * before being ready to receive a command.  Default is 0ms - meaning the sensor is
 * ready for a command by the end of the 12ms break.  Per protocol, the wake time must
 * be less than 100 ms.
 */
#define SDI12_WAKE_DELAY 0
#endif

#ifndef SDI12_YIELD_MS
/**
 * @brief The time to delay, in milliseconds, to allow the buffer to fill before
 * returning the value from the buffer.
 *
 * This may be needed for faster processors to account for the slow baud rate of SDI-12.
 * Without this, the available() function may return 0 while we're in the middle of
 * reading a character.
 *
 * There are 8.33 ms/character, so we delay by 8ms for fast processors to allow one
 * character to finish.
 */
#if F_CPU >= 48000000L
#define SDI12_YIELD_MS 8
#else
#define SDI12_YIELD_MS 0
#endif
#endif

#ifndef SDI12_YIELD
/**
 * @brief A delay function to allow the buffer to fill before returning the value from
 * the buffer.
 *
 * This may be needed for faster processors to account for the slow baud rate of SDI-12.
 */
#define SDI12_YIELD() \
  { delay(SDI12_YIELD_MS); }
#endif

#if defined(PARTICLE) || defined(ESP8266) ||          \
  (defined(ESP32) && !defined(ESP_ARDUINO_VERSION) && \
   !defined(ESP_ARDUINO_VERSION_VAL))
#define NEED_LOOKAHEAD_ENUM
#endif
#if (defined(ESP32) && defined(ESP_ARDUINO_VERSION_VAL) && defined(ESP_ARDUINO_VERSION))
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 5)
// do nothing
#else
#define NEED_LOOKAHEAD_ENUM
#endif
#endif
#if defined NEED_LOOKAHEAD_ENUM
/**
 * @brief This enumeration provides the lookahead options for parseInt(), parseFloat().
 *
 * The rules set out here are used until either the first valid character is found or a
 * time out occurs due to lack of input.
 *
 * This enum is part of the Stream parent class, but is missing from the ESP8266 core
 * and ESP32 cores prior to 3.0 (IDF prior to 5.1).
 */
enum LookaheadMode {
  /** All invalid characters are ignored. */
  SKIP_ALL,
  /** Nothing is skipped, and the stream is not touched unless the first waiting
     character is valid. */
  SKIP_NONE,
  /** Only tabs, spaces, line feeds & carriage returns are skipped.*/
  SKIP_WHITESPACE
};
#endif  // NEED_LOOKAHEAD_ENUM
#undef NEED_LOOKAHEAD_ENUM

/**
 * @brief The main class for SDI 12 instances
 */
class SDI12 : public Stream {
  /**
   * @anchor sdi12_statics
   * @name Static member variables
   *
   * @brief These are constants that apply to all SDI-12 instances.
   */
  /**@{*/
 private:
  /**
   * @brief static pointer to active SDI12 instance
   */
  static SDI12* _activeObject;
  /**
   * @brief The SDI12Timer instance to use for checking bit reception times.
   */
  static SDI12Timer sdi12timer;

  /**
   * @brief Stores the time of the previous RX transition in micros
   */
  sdi12timer_t prevBitTCNT;
  /**
   * @brief Tracks how many bits are accounted for on an incoming character.
   *
   * - if 0: indicates that we got a start bit
   * - if >0: indicates the number of bits received
   *
   * 0 - got start bit
   * 1 - got data bit 0
   * 2 - got data bit 1
   * 3 - got data bit 2
   * 4 - got data bit 3
   * 5 - got data bit 4
   * 6 - got data bit 5
   * 7 - got data bit 6
   * 8 - got data bit 7 (parity)
   * 9 - got stop bit
   * 255 - waiting for next start bit
   */
  uint8_t rxState;
  /**
   * @brief a bit mask for building a received character
   *
   * The mask has a single bit set, in the place of the active bit based on the
   * #rxState.
   */
  uint8_t rxMask;
  /**
   * @brief the value of the character being built
   */
  uint8_t rxValue;
  /**@}*/


  /**
   * @anchor sdi12_buffer
   * @name Buffer Setup
   *
   * @brief Creating a circular buffer for incoming data.
   *
   * The buffer is used to store characters from the SDI-12 data line.  Characters are
   * read into the buffer when an interrupt is received on the data line. The buffer
   * uses a circular implementation with pointers to both the head and the tail. All
   * SDI-12 instances share the same buffer.
   *
   * The default buffer size is the maximum length of a response to a normal SDI-12
   * command, which is 81 characters:
   * - address is a single (1) character
   * - values has a maximum value of 75 characters
   * - CRC is 3 characters
   * - CR is a single character
   * - LF is a single character
   *
   * For more information on circular buffers:
   * http://en.wikipedia.org/wiki/Circular_buffer
   */
  /**@{*/
 private:
  /**
   * @brief A single incoming character buffer for ALL SDI-12 objects (Rx buffer)
   *
   * Increasing the buffer size will use more RAM. If you exceed 256 characters, be sure
   * to change the data type of the index to support the larger range of addresses.  To
   * adjust the size of the buffer, change the value of `SDI12_BUFFER_SIZE` in the
   * header file.
   */
  static uint8_t _rxBuffer[SDI12_BUFFER_SIZE];
  /**
   * @brief Index of buffer head. (unsigned 8-bit integer, can map from 0-255)
   */
  static volatile uint8_t _rxBufferTail;
  /**
   * @brief Index of buffer tail. (unsigned 8-bit integer, can map from 0-255)
   */
  static volatile uint8_t _rxBufferHead;
  /**
   * @brief The buffer overflow status
   */
  bool _bufferOverflow = false;
  /**@}*/


  /**
   * @anchor reading_buffer
   * @name Reading from the SDI-12 Buffer
   *
   * @brief These functions are for reading incoming data stored in the SDI-12 buffer.
   *
   * @see <a href="class_s_d_i12.html#buffer-setup">Buffer Setup</a>
   *
   * @note peakNextDigit(), parseInt() and parseFloat() are fully implemented in the
   * parent Stream class but we don't want to them use as they are inherited.  Although
   * they are not virtual and cannot be overridden, recreating them here hides the
   * stream default versions to allow for a custom timeout return value.  The default
   * value for the Stream class is to return 0.  This makes distinguishing timeouts from
   * true zero readings impossible. Therefore the default value has been set to -9999 in
   * the being function.  The value returned by a timeout (SDI12::TIMEOUT) is a public
   * variable and can be changed dynamically within a program by calling
   * `mySDI12.TIMEOUT = (int) newValue` or using SDI12::setTimeoutValue(int16_t value).
   *
   */
  /**@{*/
 public:
  /**
   * @brief Return the number of bytes available in the Rx buffer
   *
   * @return The number of characters in the buffer
   *
   * available() is a public function that returns the number of characters available in
   * the Rx buffer.
   *
   * To understand how:
   * `_rxBufferTail + SDI12_BUFFER_SIZE - _rxBufferHead) % SDI12_BUFFER_SIZE;`
   * accomplishes this task, we will use a few examples.
   *
   * To start take the buffer below that has `SDI12_BUFFER_SIZE = 10`. The message
   * "abc" has been wrapped around (circular buffer).
   *
   * @code{.cpp}
   *     _rxBufferTail = 1 // points to the '-' after c
   *     _rxBufferHead = 8 // points to 'a'
   * @endcode
   *
   * [ c ] [ - ] [ - ] [ - ] [ - ] [ - ] [ - ] [ - ]  [ a ] [ b ]
   *
   * The number of available characters is (1 + 10 - 8) % 10 = 3
   *
   * The '%' or modulo operator finds the remainder of division of one number by
   * another. In integer arithmetic 3 / 10 = 0, but has a remainder of 3.  We can only
   * get the remainder by using the the modulo '%'. 3 % 10 = 3.  This next case
   * demonstrates more clearly why the modulo is used.
   *
   * @code{.cpp}
   *     _rxBufferTail = 4 // points to the '-' after c
   *     _rxBufferHead = 1 // points to 'a'
   * @endcode
   *
   * [ a ] [ b ] [ c ] [ - ] [ - ] [ - ] [ - ] [ - ]  [ - ] [ - ]
   *
   * The number of available characters is (4 + 10 - 1) % 10 = 3
   *
   * If we did not use the modulo we would get either ( 4 + 10 - 1 ) = 13 characters or
   * ( 4 + 10 - 1 ) / 10 = 1 character. Obviously neither is correct.
   *
   * If there has been a buffer overflow, available() will return -1.
   */
  int available() override;
  /**
   * @brief Reveal next byte in the Rx buffer without consuming it.
   *
   * @return The next byte in the character buffer.
   *
   * peek() is a public function that allows the user to look at the character that is
   * at the head of the buffer. Unlike read() it does not consume the character (i.e.
   * the index addressed by _rxBufferHead is not changed). peek() returns -1 if there
   * are no characters to show.
   */
  int peek() override;
  /**
   * @brief Clear the Rx buffer by setting the head and tail pointers to the same value.
   *
   * clearBuffer() is a public function that clears the buffers contents by setting the
   * index for both head and tail back to zero.
   */
  void clearBuffer();
  /**
   * @brief Return next byte in the Rx buffer, consuming it
   *
   * @return The next byte in the character buffer.
   *
   * read() returns the character at the current head in the buffer after incrementing
   * the index of the buffer head. This action 'consumes' the character, meaning it can
   * not be read from the buffer again. If you would rather see the character, but leave
   * the index to head intact, you should use peek();
   */
  int read() override;

  /**
   * @brief Wait for sending to finish - because no TX buffering and the write function
   * is blocking, we don't need to do anything.
   */
  void flush() override {}

  /**
   * @brief Return the first valid (long) integer value from the current position.
   *
   * This function is customized to only return numbers as they are passed in the data
   * command responses.
   *
   * A data command response is structured <addr><values><CR><LF> or
   * <addr><values><CRC><CR><LF> the value portion must be structred as pd.d
   * - p - the polarity sign (+ or -)
   * - d - numeric digits before the decimal place
   * - . - the decimal point (optional)
   * - d - numeric digits after the decimal point
   * - the maximum number of digits for a data value is 7, even without a decimal point
   * - the minimum number of digits for a data value (excluding the decimal point) is 1
   * - the maximum number of characters in a data value is 9 (the (polarity sign + 7
   * digits + the decimal point))
   * - The polarity symbol (+ or -) acts as a delimeter between the numeric values
   *
   * Because of the well codified structure of the response, we know that we can always
   * set the LookaheadMode to not skip any characters (LookaheadMode::SKIP_NONE), we
   * accept a + or - only as the first character, and we should not ignore any other
   * characters.
   *
   * @param warning Any input LookaheadMode or ignore character will be ignored by this
   * function!
   * @return The next valid integer in the stream or -9999 if there is a timeout or the
   * next character is not part of an integer.
   *
   * @note This function _hides_ the Stream class function to allow a custom value to be
   * returned on timeout.  It cannot overwrite the Stream function because it is not
   * virtual.
   */
  long parseInt(LookaheadMode = SKIP_NONE, char = '+');

  /**
   * @brief Return the first valid float value from the current position.
   *
   * This is identical to SDI12::parseInt(LookaheadMode, char) except that it looks for
   * a decimal and returns a float.
   *
   * @return The first valid float in the stream or -9999 if there is a timeout or the
   * next character is not part of an float.
   *
   * @note This function _hides_ the Stream class function to allow a custom value to be
   * returned on timeout.  It cannot overwrite the Stream function because it is not
   * virtual.
   * @see @ref SDI12::parseInt(LookaheadMode, char)
   */
  float parseFloat(LookaheadMode = SKIP_NONE, char = '+');

 protected:
  /**
   * @brief Return the next numeric digit in the stream or -1 if timeout
   *
   * @param lookahead the mode to use to look ahead in the
   * stream
   * @param detectDecimal True to accept a decimal point ('.') as part of a
   * number
   * @return The next numeric digit in the stream
   *
   * @note This peekNextDigit function is almost identical to the Stream version, but it
   * accepts a "+" as the start of a digit.
   */
  int peekNextDigit(LookaheadMode lookahead, bool detectDecimal);
  /**@}*/


  /**
   * @anchor ctor
   * @name Constructor, Destructor, Begins, and Setters
   *
   * @brief These functions set up the SDI-12 object and prepare it for use.
   */
  /**@{*/
 private:
  /**
   * @brief reference to the data pin
   */
  int8_t _dataPin = -1;

 public:
  /**
   * @brief Construct a new SDI12 instance with no data pin set.
   *
   * Before using the SDI-12 instance, the data pin must be set with
   * SDI12::setDataPin(dataPin) or SDI12::begin(dataPin). This empty constructor is
   * provided for easier integration with other Arduino libraries.
   *
   * When the constructor is called it resets the buffer overflow status to FALSE.
   */
  SDI12();
  /**
   * @brief Construct a new SDI12 with the data pin set
   *
   * @param dataPin The data pin's digital pin number
   *
   * When the constructor is called it resets the buffer overflow status to FALSE and
   * assigns the pin number "dataPin" to the private variable "_dataPin".
   */
  explicit SDI12(int8_t dataPin);
  /**
   * @brief Destroy the SDI12 object.
   *
   * When the destructor is called, it's main task is to disable any interrupts that had
   * been previously assigned to the pin, so that the pin will behave as expected when
   * used for other purposes. This is achieved by putting the SDI-12 object in the
   * SDI12_DISABLED state.  After disabling interrupts, the pointer to the current
   * active SDI-12 instance is set to null if it had pointed to the destroyed object.
   * Finally, for AVR board, the timer prescaler is set back to whatever it had been
   * prior to creating the SDI-12 object.
   */
  ~SDI12();
  /**
   * @brief Begin the SDI-12 object.
   *
   * This is called to begin the functionality of the SDI-12 object.  It sets the object
   * as the active object, sets the stream timeout to 150ms to match SDI-12 specs, sets
   * the timeout return value to SDI12::TIMEOUT, and configures the timer prescaler.
   */
  void begin();
  /**
   * @brief Set the SDI12::_datapin and begin the SDI-12 object.
   *
   * @copydetails SDI12::begin()
   * If the SDI-12 instance is created using the empty constructor, this must be used
   * to set the data pin.
   *
   * @param dataPin The data pin's digital pin number
   */
  void begin(int8_t dataPin);
  /**
   * @brief Disable the SDI-12 object (but do not destroy it).
   *
   * Set the SDI-12 state to disabled, set the pointer to the current active instance
   * to null, and then, for AVR boards, unset the timer prescaler.
   *
   * This can be called to temporarily cease all functionality of the SDI-12 object. It
   * is not as harsh as destroying the object with the destructor, as it will maintain
   * the memory buffer.
   */
  void end();
  /**
   * @brief The value to return if a parse or read times out with no return from the
   * sensor.
   *
   * The timeout return for an Arduino stream object when no character is available in
   * the Rx buffer is "0."  For environmental sensors (the typical SDI-12 users) 0 is a
   * common result value.  To better distinguish between a timeout because of no
   * sensor response and a true zero return, the timeout should be set to some value
   * that is NOT a possible return from that sensor.  If the timeout is not set, -9999
   * is used.
   */
  int16_t TIMEOUT;
  /**
   * @brief Set the value to return if a parse int or parse float times out with no
   * return from the sensor.
   *
   * The "standard" timeout return for an Arduino stream object when no character is
   * available in the Rx buffer is "0."  For environmental sensors (the typical SDI-12
   * users) 0 is a common result value.  To better distinguish between a timeout because
   * of no sensor response and a true zero return, the timeout should be set to some
   * value that is NOT a possible return from that sensor.  If the timeout is not set,
   * -9999 is used.
   *
   * @param value the value to return on timeout
   */
  void setTimeoutValue(int16_t value);
  /**
   * @brief Get the data pin for the current SDI-12 instance
   *
   * @return The data pin number
   */
  int8_t getDataPin();
  /**
   * @brief Set the data pin for the current SDI-12 instance
   *
   * @param dataPin  The data pin's digital pin number
   */
  void setDataPin(int8_t dataPin);
#ifdef SDI12_CHECK_PARITY
  bool _parityFailure;
#endif
  /**@}*/


  /**
   * @anchor multiple_objects
   * @name Using more than one SDI-12 Object
   *
   * @brief Functions needed for multiple instances of the SDI12 class.
   *
   * This library is allows for multiple instances of itself running on the same or
   * different pins.  SDI-12 can support up to 62 sensors on a single pin/bus, so it is
   * not necessary to use an instance for each sensor.
   *
   * Because we are using pin change interrupts there can only be one active object at a
   * time (since this is the only reliable way to determine which pin the interrupt
   * occurred on).  The active object is the only object that will respond properly to
   * interrupts.  However promoting another instance to Active status does not
   * automatically remove the interrupts on the other pin. For proper behavior it is
   * recommended to use this pattern:
   *
   * @code{.cpp}
   *     mySDI12.forceHold();
   *     myOtherSDI12.setActive();
   * @endcode
   *
   * @note
   * - Promoting an object into the Active state will set it as `SDI12_HOLDING`.
   * - Calling mySDI12.begin() will assert mySDI12 as the new active object, until
   * another instance calls myOtherSDI12.begin() or myOtherSDI12.setActive().
   * - Calling mySDI12.end() does NOT hand-off active status to another SDI-12 instance.
   * - You can check on the active object by calling mySDI12.isActive(), which will
   * return a boolean value TRUE if active or FALSE if inactive.
   */
  /**@{*/
 public:
  /**
   * @brief Set this instance as the active SDI-12 instance
   *
   * @return True indicates that the current SDI-12
   * instance was not formerly the active one and now is.  False indicates that the
   * current SDI-12 instance *is already the active one* and the state was not changed.
   *
   * A method for setting the current object as the active object; returns TRUE if
   * the object was not formerly the active object and now is.
   * - Promoting an inactive to the active instance will start it in the SDI12_HOLDING
   * state and return TRUE.
   * - Otherwise, if the object is currently the active instance, it will remain
   * unchanged and return FALSE.
   */
  bool setActive();

  /**
   * @brief Check if this instance is active
   *
   * @return True indicates that the curren SDI-12
   * instance is the active one.
   *
   * isActive() is a method for checking if the object is the active object.  Returns
   * true if the object is currently the active object, false otherwise.
   */
  bool isActive();
  /**@}*/


  /**
   * @anchor line_states
   * @name Data Line States
   *
   * @brief Functions for maintaining the proper data line state.
   *
   * The Arduino is responsible for managing communication with the sensors.  Since all
   * the data transfer happens on the same line, the state of the data line is very
   * important.
   *
   * @section line_state_spec Specifications
   *
   * Per the SDI-12 specification, the voltage ranges for SDI-12 are:
   *
   * - When the pin is in the SDI12_HOLDING state, it is holding the line LOW so that
   * interference does not unintentionally wake the sensors up.  The interrupt is
   * disabled for the dataPin, because we are not expecting any SDI-12 traffic.
   * - In the SDI12_TRANSMITTING state, we would like exclusive control of the Arduino,
   * so we shut off all interrupts, and vary the voltage of the dataPin in order to wake
   * up and send commands to the sensor.
   * - In the SDI12_LISTENING state, we are waiting for a sensor to respond, so we drop
   * the voltage level to LOW and relinquish control (INPUT).
   * - If we would like to disable all SDI-12 functionality, then we set the system to
   * the SDI12_DISABLED state, removing the interrupt associated with the dataPin.  For
   * predictability, we set the pin to a LOW level high impedance state (INPUT).
   *
   * @section line_state_table As a Table
   *
   * Summarized in a table:
   *
   * | State               | Interrupts       | Pin Mode   | Pin Level |
   * |---------------------|------------------|------------|-----------|
   * | SDI12_DISABLED      | Pin Disable      | INPUT      | ---       |
   * | SDI12_ENABLED       | Pin Disable      | INPUT      | ---       |
   * | SDI12_HOLDING       | Pin Disable      | OUTPUT     | LOW       |
   * | SDI12_TRANSMITTING  | All/Pin Disable  | OUTPUT     | VARYING   |
   * | SDI12_LISTENING     | All Enable       | INPUT      | ---       |
   *
   *
   * @section line_state_seq Sequencing
   *
   * Generally, this flow of line states is acceptable:
   *
   * `HOLDING --> TRANSMITTING --> LISTENING --> TRANSMITTING --> LISTENING`
   *
   * If you have interference, you should force a hold, using forceHold().
   * The flow would then be:
   *
   * `HOLDING --> TRANSMITTING --> LISTENING -->` done reading, forceHold() `--->
   * HOLDING`
   *
   * @see For a detailed explanation of interrupts see @ref interrupts_page
   */
  /**@{*/
 private:
  /**
   * @brief The various SDI-12 line states.
   */
  typedef enum SDI12_STATES {
    /** SDI-12 is disabled, pin mode INPUT, interrupts disabled for the pin */
    SDI12_DISABLED,
    /** SDI-12 is enabled, pin mode INPUT, interrupts disabled for the pin */
    SDI12_ENABLED,
    /** The line is being held LOW, pin mode OUTPUT, interrupts disabled for the pin */
    SDI12_HOLDING,
    /** Data is being transmitted by the SDI-12 master, pin mode OUTPUT, interrupts
       disabled for the pin */
    SDI12_TRANSMITTING,
    /** The SDI-12 master is listening for a response from the slave, pin mode INPUT,
       interrupts enabled for the pin */
    SDI12_LISTENING
  } SDI12_STATES;

#ifndef __AVR__
  /**
   * @brief Calculate the parity value for a character using even parity.
   *
   * @param v **uint8_t (char)** the character to calculate the parity of
   * @return The input character with the 8th bit set
   * to the even parity value for that character
   *
   * Sets up parity and interrupts for different processor types - that is, imports the
   * interrupts and parity for the AVR processors where they exist.
   *
   * This function is defined in the Arduino core for AVR processors, but must be
   * defined here for SAMD and ESP cores.
   */
  static uint8_t parity_even_bit(uint8_t v);
#endif

  /**
   * @brief Set the pin interrupts to be on (enabled) or off (disabled)
   *
   * @param enable True to enable pin interrupts
   *
   * A private helper function to turn pin interrupts on or off
   */
  void setPinInterrupts(bool enable);
  /**
   * @brief Set the the state of the SDI12 object[s]
   *
   * @param state The state the SDI-12 object should be set to, from
   * the SDI12_STATES enum.
   *
   * This is a private function, and only used internally.
   */
  void setState(SDI12_STATES state);

 public:
  /**
   * @brief Set line state to SDI12_HOLDING
   *
   * A public function which forces the line into a "holding" state. This is generally
   * unneeded, but for deployments where interference is an issue, it should be used
   * after all expected bytes have been returned from the sensor.
   */
  void forceHold();
  /**
   * @brief Set line state to SDI12_LISTENING
   *
   * A public function which forces the line into a "listening" state.  This may be
   * needed for implementing a slave-side device, which should relinquish control of the
   * data line when not transmitting.
   */
  void forceListen();
  /**@}*/


  /**
   * @anchor communication
   * @name Waking Up and Talking To Sensors
   *
   * @brief These functions are needed to communicate with SDI-12 sensors (slaves) or an
   * SDI-12 data logger (master).
   */
  /**@{*/
 private:
  /**
   * @brief Used to wake up the SDI12 bus.
   *
   * @param extraWakeTime The amount of additional time in milliseconds that the sensor
   * takes to wake before being ready to receive a command.  Default is 0ms - meaning
   * the sensor is ready for a command by the end of the 12ms break.  Should be lower
   * than 100.
   *
   * Wakes up all the sensors on the bus.  Set the SDI-12 state to transmitting, hold
   * the data line high for the required break of 12 milliseconds plus any needed
   * additional delay to allow the sensor to wake, then hold the line low for the
   * required marking of 8.33 milliseconds.
   *
   * The SDI-12 protocol requires a pulse of HIGH voltage for at least 12 milliseconds
   * (the break) followed immediately by a pulse of LOW voltage for at least 8.33, but
   * not more than 100, milliseconds. Setting the SDI-12 object into the
   * SDI12_TRANSMITTING allows us to assert control of the line without triggering any
   * interrupts.
   *
   * Per specifications:
   * > • A data recorder transmits a break by setting the data line to spacing for at
   * > least 12 milliseconds.
   * >
   * > • The sensor will not recognize a break condition for a continuous spacing time
   * > of less than 6.5 milliseconds and will always recognize a break when the line is
   * > continuously spacing for more than 12 milliseconds.
   *
   * > • Upon receiving a break, a sensor must detect 8.33 milliseconds of marking on
   * > the data line before it looks for an address.
   * >
   * > • A sensor must wake up from a low-power standby mode and be capable of detecting
   * > a start bit from a valid command within 100 milliseconds after detecting a break
   * >
   * > • Sensors must return to a low-power standby mode after receiving an invalid
   * > address or after detecting a marking state on the data line for 100 milliseconds.
   * > (Tolerance:    +0.40 milliseconds.)
   */
  void wakeSensors(int8_t extraWakeTime = 0);
  /**
   * @brief Used to send a character out on the data line
   *
   * @param out **uint8_t (char)** the character to write
   *
   * This function writes a character out to the data line.  SDI-12 specifies the
   * general transmission format of a single character as:
   * - 10 bits per data frame
   *     - 1 start bit
   *     - 7 data bits (least significant bit first)
   *     - 1 even parity bit
   *     - 1 stop bit
   *
   * Recall that we are using inverse logic, so HIGH represents 0, and LOW represents
   * a 1.
   */
  void writeChar(uint8_t out);

 public:
  /**
   * @brief Write out a byte on the SDI-12 line
   *
   * @param byte The character to write
   * @return The number of characters written
   *
   * Sets the state to transmitting, writes a character, and then sets the state back to
   * listening.  This function must be implemented as part of the Arduino Stream
   * instance, but is *NOT* intended to be used for SDI-12 objects.  Instead, use the
   * SDI12::sendCommand() or SDI12::sendResponse() functions.
   */
  virtual size_t write(uint8_t byte);

  /**
   * @brief Send a command out on the data line, acting as a data logger (master)
   *
   * @param cmd the command to send
   *
   * A publicly accessible function that sends a break to wake sensors and sends out a
   * command byte by byte on the data line.
   *
   * @param extraWakeTime The amount of additional time in milliseconds that the sensor
   * takes to wake before being ready to receive a command.  Default is 0ms - meaning
   * the sensor is ready for a command by the end of the 12ms break.  Per protocol, the
   * wake time must be less than 100 ms.
   */
  void sendCommand(String& cmd, int8_t extraWakeTime = SDI12_WAKE_DELAY);
  /// @copydoc SDI12::sendCommand(String&, int8_t)
  void sendCommand(const char* cmd, int8_t extraWakeTime = SDI12_WAKE_DELAY);
  /// @copydoc SDI12::sendCommand(String&, int8_t)
  void sendCommand(FlashString cmd, int8_t extraWakeTime = SDI12_WAKE_DELAY);

  /**
   * @brief Calculates the 16-bit Cyclic Redundancy Check (CRC) for an SDI-12 message.
   *
   * @param resp The message to calculate the CRC for.
   * @return *uint16_t* The calculated CRC
   */
  uint16_t calculateCRC(String& resp);
  /// @copydoc SDI12::calculateCRC(String&)
  uint16_t calculateCRC(const char* resp);
  /// @copydoc SDI12::calculateCRC(String&)
  uint16_t calculateCRC(FlashString resp);

  /**
   * @brief Converts a numeric 16-bit CRC to an ASCII String.
   *
   * From the SDI-12 Specifications:
   *
   *     The 16 bit CRC is encoded as three ASCII characters
   *     using the following algorithm:
   *         1st character = 0x40 OR (CRC shifted right 12 bits)
   *         2nd character = 0x40 OR ((CRC shifted right 6 bits) AND 0x3F)
   *         3rd character = 0x40 OR (CRC AND 0x3F)
   *
   * @param crc The 16-bit CRC
   * @return *String* An ASCII string for the CRC
   */
  String crcToString(uint16_t crc);

  /**
   * @brief Verifies that the CRC returned at the end of an SDI-12 message matches that
   * of the content of the message.
   *
   * @param respWithCRC The full SDI-12 message, including the CRC at the end.
   * @return True if the CRC matches and the message is valid, false if the CRC doesn't
   * match and the message could be retried.
   */
  bool verifyCRC(String& respWithCRC);

  /**
   * @brief Send a response out on the data line (for slave use)
   *
   * @param resp the response to send
   * @param addCRC True to append a CRC to the outgoing response
   *
   * A publicly accessible function that sends out an 8.33 ms marking and a response
   * byte by byte on the data line.  This is needed if the Arduino is acting as an
   * SDI-12 device itself, not as a recorder for another SDI-12 device.
   */
  void sendResponse(String& resp, bool addCRC = false);
  /// @copydoc SDI12::sendResponse(String& resp, bool addCRC)
  void sendResponse(const char* resp, bool addCRC = false);
  /// @copydoc SDI12::sendResponse(String& resp, bool addCRC)
  void sendResponse(FlashString resp, bool addCRC = false);
  ///@}

  /**
   * @anchor interrupt_fxns
   * @name Interrupt Service Routine
   *
   * @brief Functions for handling interrupts - responding to changes on the data line
   * and converting them to characters in the Rx buffer.
   *
   * @see For a detailed explanation of interrupts see @ref interrupts_page
   */
  /**@{*/
 private:
  /**
   * @brief Creates a blank slate for a new incoming character
   */
  void startChar();
  /**
   * @brief The interrupt service routine (ISR) - the function responding to changes in
   * rx line state.
   *
   * This function checks which direction the change of the interrupt was and then uses
   * that to populate the bits of the character. Unlike SoftwareSerial which listens for
   * a start bit and then halts all program and other ISR execution until the end of the
   * character, this library grabs the time of the interrupt, does some quick math, and
   * lets the processor move on.  The logic of creating a character this way is harder
   * for a person to follow, but it pays off because we're not tieing up the processor
   * in an ISR that lasts for 8.33ms for each character. [10 bits @ 1200 bits/s] For a
   * person, that 8.33ms is trivial, but for even a "slow" 8MHz processor, that's over
   * 60,000 ticks sitting idle per character.
   */
  void receiveISR();
  /**
   * @brief Put a finished character into the SDI12 buffer
   *
   * @param c **uint8_t (char)** the character to add to the buffer
   */
  void charToBuffer(uint8_t c);

 public:
  /**
   * @brief Intermediary used by the ISR - passes off responsibility for the interrupt
   * to the active object.
   *
   * On espressif boards (ESP8266 and ESP32), the ISR must be stored in IRAM
   */
  static void handleInterrupt();

  /** on AVR boards, uncomment to use your own PCINT ISRs */
  // #define SDI12_EXTERNAL_PCINT
  /**@}*/
};

#endif  // SRC_SDI12_H_
