/*
 RS485 protocol library - non-blocking.
 
 Devised and written by Nick Gammon.
 Date: 4 December 2012
 Version: 1.0
 
 Licence: Released for public use.
 
*/

#ifndef RS485_nonBlocking_h
#define RS485_nonBlocking_h


class RS485 
{

  typedef size_t (*WriteCallback)  (const uint8_t what);    // send a byte to serial port
  typedef int  (*AvailableCallback)  ();    // return number of bytes available
  typedef int  (*ReadCallback)  ();    // read a byte from serial port

  enum {
        STX = '\2',   // start of text
        ETX = '\3'    // end of text
  };  // end of enum

  // callback functions to do reading/writing
  ReadCallback fReadCallback_;
  AvailableCallback fAvailableCallback_;
  WriteCallback fWriteCallback_; 
  
  // how much data is in the buffer
  const int bufferSize_;
  
  // where we save incoming stuff
  uint8_t * data_;
    
  // this is true once we have valid data in buf
  bool available_;
  
  // an STX (start of text) signals a packet start
  bool haveSTX_;
  
  // count of errors
  unsigned long errorCount_;
  
  // variables below are set when we get an STX
  bool haveETX_;
  uint8_t inputPos_;
  uint8_t currentByte_;
  bool firstNibble_;
  unsigned long startTime_;

  // helper private functions
  uint8_t crc8 (const uint8_t *addr, uint8_t len);
  void sendComplemented (const uint8_t what);

  public:
    
    // constructor
    RS485 (ReadCallback fReadCallback, AvailableCallback fAvailableCallback, WriteCallback fWriteCallback, uint8_t bufferSize) :
        fReadCallback_ (fReadCallback), 
        fAvailableCallback_ (fAvailableCallback), 
        fWriteCallback_ (fWriteCallback),
        bufferSize_ (bufferSize),
        data_ (NULL) {}
  
    // destructor - frees memory used
    ~RS485 () { stop (); }
  
    // allocate memory for buf_
    void begin ();
    
    // free memory in buf_
    void stop ();
  
    // handle incoming data, return true if packet ready
    bool update ();
  
    // reset to no incoming data (eg. after a timeout)
    void reset ();
  
    // send data
    void sendMsg (const uint8_t * data, const uint8_t length);
  
    // returns true if packet available
    bool available () const { return available_; };
  
    // once available, returns the address of the current message
     uint8_t * getData ()    { return data_; }
     uint8_t   getLength ()  { return inputPos_; }
    
    // return how many errors we have had
    unsigned long getErrorCount () const { return errorCount_; }
  
    // return when last packet started
    unsigned long getPacketStartTime () const { return startTime_; }
  
    // return true if a packet has started to be received
    bool isPacketStarted () const { return haveSTX_; }
  
}; // end of class RS485

#endif