//******************************************************************************************
//  File: PS_PulseCounter.cpp
//  Authors: Dan G Ogorchock & Daniel J Ogorchock (Father and Son)
//
//  Summary:  PS_PulseCounter is a class which implements the SmartThings "Power Meter"-style device capability.
//			  It inherits from the st::PollingSensor class.  The current version uses a digital input to measure the 
//			  the number of counts between polling intervals.  At the polling interval, the pulse count is converted
//			  to engineering units via a linear conversion (engUnits = slope x counts + offset).
//
//
//			  Create an instance of this class in your sketch's global variable section
//			  For Example:  st::PS_PulseCounter sensor3(F("power1"), 60, 5, PIN_PULSE, FALLING, INPUT_PULLUP, 1.0, 0);
//
//			  st::PS_PulseCounter() constructor requires the following arguments
//				- String &name - REQUIRED - the name of the object - must be in form of "power1", "power2", etc...
//				- int interval - REQUIRED - the polling interval in seconds
//				- int offset - REQUIRED - the polling interval offset in seconds - used to prevent all polling sensors from executing at the same time
//				- byte pin - REQUIRED - the GPIO Pin to be used as an digital input (Hardware Interrupt)
//				- byte inttype - REQUIRED - which type of Arduino interrupt to trigger the ISR (RISING, FALLING, CHANGE)
//				- byte inputmode - REQUIRED - Mode of the digital input Pin (INPUT, INPUT_PULLUP)
//				- float cnvslope - REQUIRED - Conversion to Engineering Units Slope
//				- float cnvoffset - REQUIRED - Conversion to Engineering Units Offset
//
//			  This class supports receiving configuration data from the SmartThings cloud via the ST App.  A user preference
//			  can be configured in your phone's ST App, and then the "Configure" tile will send the data for all sensors to 
//			  the ST Shield.  For PollingSensors, this data is handled in the beSMart() function.
//
//			  TODO:  Determine a method to persist the ST Cloud's Polling Interval data
//
//  Change History:
//
//    Date        Who            What
//    ----        ---            ----
//    2015-03-31  Dan Ogorchock  Original Creation
//    2018-03-03  Dan Ogorchock  Improved code to make generic for all boards, not just Arduino MEGA
//    2020-01-17  Dan Ogorchock  Improved support for ESP8266 using Arduino IDE Board Manager 2.5.1 and newer
//    2020-11-15  Dan Ogorchock  Prevent Refresh from sending data for this particular device.
//    2021-04-12  Dan Ogorchock  Corrected data type for interrupt type to correct compiler error for Nano 33 IoT
//    2021-06-14  Dan Ogorchock  Fixed for SAMD Architectures...again
//    2023-01-25  Dan Ogorchock  Fixed for MKR 1010 (use PinStatus instead of int for inttype)
//    2023-01-26  Dan Ogorchock  Fixed for SAMD Architecture boards versus all other boards
//
//
//******************************************************************************************

#include "PS_PulseCounter.h"

#include "Constants.h"
#include "Everything.h"
//#include "PinChangeInt.h"

namespace st
{
	//private

	//This "Counts" variables must be declared here so they can be used in the Interrupt Service Routines (ISR)
	volatile unsigned long m_nCounts; //current count of interrupts (pulses)

	//These are the four Interrupt Service Routines (ISR) which must be unique for each interrupt
#if defined(ARDUINO_ARCH_ESP8266)
	void ICACHE_RAM_ATTR isrPulse() {
#else 
void isrPulse() {
#endif
	m_nCounts++;
	}

//public

	//constructor - called in your sketch's global variable declaration section
#if defined(ARDUINO_ARCH_SAMD)
	PS_PulseCounter::PS_PulseCounter(const __FlashStringHelper *name, unsigned int interval, int offset, byte inputpin, PinStatus inttype, byte inputmode, float cnvslope, float cnvoffset) :
#else
    PS_PulseCounter::PS_PulseCounter(const __FlashStringHelper* name, unsigned int interval, int offset, byte inputpin, int inttype, byte inputmode, float cnvslope, float cnvoffset) :
#endif
		PollingSensor(name, interval, offset),
		m_nInputMode(inputmode),
		m_nSensorValue(0),
		m_fCnvSlope(cnvslope),
		m_fCnvOffset(cnvoffset)
	{
		setPin(inputpin);
		m_nCounts = 0;
		m_pCounter = &m_nCounts;
		attachInterrupt(digitalPinToInterrupt(m_nInputPin), isrPulse, inttype);	
	}
	
	//destructor
	PS_PulseCounter::~PS_PulseCounter()
	{
		
	}

	//SmartThings Shield data handler (receives configuration data from ST - polling interval, and adjusts on the fly)
	void PS_PulseCounter::beSmart(const String &str)
	{
		String s = str.substring(str.indexOf(' ') + 1);
		
		if (s.toInt() != 0) {
			st::PollingSensor::setInterval(s.toInt() * 1000);
			if (st::PollingSensor::debug) {
				Serial.print(F("PS_PulseCounter::beSmart set polling interval to "));
				Serial.println(s.toInt());
			}
		}
		else {
			if (st::PollingSensor::debug)
			{
				Serial.print(F("PS_PulseCounter::beSmart cannot convert "));
				Serial.print(s);
				Serial.println(F(" to an Integer."));
			}
		}
	}
	
	void PS_PulseCounter::refresh()
	{
		//This specific device should not report data except during its scheduled polling interval to preserve data integrity of the pulse counted value
		//getData();
	}
	
	//function to get data from sensor and queue results for transfer to ST Cloud
	void PS_PulseCounter::getData()
	{
		if (m_pCounter)
		{
			noInterrupts();
				unsigned long tmpCounts = *m_pCounter;
				*m_pCounter = 0;
			interrupts();

			m_nSensorValue = long(m_fCnvSlope * tmpCounts + m_fCnvOffset);

		}
		else  //invalid Pin/Interrupt was requested, therefore we are in an error condition
		{
			m_nSensorValue = 0;
			if (st::PollingSensor::debug) {
				Serial.println(F("PS_PulseCounter::Something went wrong. Need to debug."));
			}
		}

		Everything::sendSmartString(getName() + " " + m_nSensorValue);
	}

	void PS_PulseCounter::setPin(byte pin)
	{
		m_nInputPin = pin;
		pinMode(m_nInputPin, m_nInputMode);
	}
}