/*
  DeviceID - read out bits from digital pins as a device identifier.
  Created by Jan Benda, September 16th, 2024.

  The idea is to give a device an identifying ID by manually setting
  DIP switches. Each switch represents a bit of a number. This way,
  the device can get an ID when it is assembled without the need of a
  computer to program an ID.  Then, at the start of a sketch, the
  DeviceID class can be used to read in this device ID. Use this ID
  for naming files, for example, via the makeStr() method.

  The switches can be directly connected between Teensy pins and
  ground. Then you should configure the Teensy pins as INPUT_PULLUP
  (default of the setPins() functions).

  Alternatively, the switches can be connected between Teensy pins and
  3.3V. Then the Teensy pins need to be connected via pull-down
  resistance to ground. Configure these pins as normal INPUT by
  setting the pullup argument in the setPins() functions to false. In
  order to reduce power consumption, the 3.3V to the switches can be
  supplied from a dedicated digital pin. Then the switches (and
  pulldown resistances) are only powered when reading out the device
  ID.
*/

#ifndef DeviceID_h
#define DeviceID_h


#include <Arduino.h>
#include <MicroConfig.h>


class DeviceID {

public:

  // Maximum number of pins (=bits) that can form a device ID.
  static const int MaxPins = 8;


  // Initialize the DeviceID class with a default device identifier.
  // The optional powerdelay is the delay in milliseconds between
  // switching on power and reading out the switches.
  DeviceID(int id=0, int powerdelay=2);

  // Return the device ID.
  int id() const { return ID; };

  // Set the device identifier to id.
  void setID(int id);

  // Return the maximum possible value of the device ID
  // (two to the power of number of pins if pins are available, otherwise 0).
  int maxid() const;

  // Write device identifier to stream.
  void write(Stream &stream=Serial, size_t indent=0,
	     size_t indent_delta=4) const;

  // Set the pins that have switches connected.
  // The first pin in the array is bit 0.
  // Terminate the array with a negative number.
  // If the switches connect the pins directly to ground,
  // then the pins need to be configured with pullup and are inverted.
  // Otherwise, they are configured as normal input and are not
  // inverted.
  void setPins(const int *pins, bool pullup=true);

  // Set the powerpin that supplies the switches with power and
  // set the pins that have switches connected.
  // The first pin in the array is bit 0.
  // Terminate the array with a negative number.
  // If the switches connect the pins directly to ground,
  // then the pins need to be configured with pullup and are inverted.
  // Otherwise, they are configured as normal input and are not
  // inverted.
  void setPins(int powerpin, const int *pins, bool pullup=true);

  // Read out the device ID from DIP switches.
  // That is, supply power via the power pin, sequentally read in the
  // states of the pins, and return the number encoded by these bits.
  // If no pins were specified, they do no set the
  // device ID and return -1.
  // On success returns the read in device ID.
  int read(Stream *stream=0);

  
protected:
  
  int ID;             // the device ID.
  int Source;         // from which source was the device identifier set:
                      // 0: none, 1: default via the constructor, 2: set via setID(), 3: read in via read().

  int NPins;          // number of pins.
  int Pins[MaxPins];  // the pins.
  bool Pullup;        // Input pins are configured as pullup and are inverted.
  int PowerPin;       // the pin providing power to the switches.
  int PowerDelay;     // the delay in ms between providing power and reading out the pins (default 2ms).

};


class DeviceIDAction : public Action {

 public:

  /* Initialize and add to configuration menu. */
  DeviceIDAction(Menu &menu, DeviceID *deviceid);

  /* Read and print device ID. */
  virtual void write(Stream &stream=Serial, unsigned int roles=AllRoles,
		     size_t indent=0, size_t width=0) const;

protected:

  DeviceID *DevID;
  
};


#endif
