#ifndef MPWMChanelHPP
#define MPWMChanelHPP
//---------------------------------------------------------------------------
#include <Wire.h> /// порт сыпет ошибками
#include <Adafruit_PWMServoDriver.h>
#include "MFunction.h"
#include "MList.hpp"
#include "MTime.hpp"
#include "MJsonSave.hpp"
#include "MJsonLoad.hpp"
#include "MEeprom.hpp"

//#include "MLight.hpp"

extern MTime Time;
extern MEeprom Eeprom;
bool bEmergy=false;
Adafruit_PWMServoDriver PWMDriver;
const int AddressPCA9685 = 0x40; /// PWM
int Frequency = 1000; /// if(_<1) than pwm disabled; for shielp pwm PCA9685
///double GpioFreq = 1E8; /// max 
//uint8_t GpioBit=3;
// Enable LEDC PWM peripheral
class MPWMChanel;
class MStCh /// for list of gpio chanels
{
public:
	signed char Bit;
	signed char Pin; 
	MPWMChanel* PWMChanel;
	void Initial()
	{
		Bit=-1;
		Pin=-1;
		PWMChanel = NULL;
	}
	MStCh()
	{
		Initial();
	}
};
MListSimple<MStCh> LChGpio; /// Count=16; LChPin[iChanels]=pin; for save info of ledcAttachPin(GPIO_LED, 0)
//---------------------------------------------------------------------------
struct MPoint
{
//public:
	unsigned long MSec; 
	float V;
	//MPoint(unsigned long MSec_, unsigned short V_)
	//{
	//	MSec=MSec_;
	//	V=V_;
	//}
};
//---------------------------------------------------------------------------
class MPWMChanel
{
public:
	enum ERegime { rAuto, rOn, rOff };		
	enum EType { tLED, tTimer, tFan, tNone};	
	enum ECh{cNone, cGpio, cDig, cPwm, cAnlg}; /// cGpio - analog GPIO, cDig - LOW/HIGH gpio, cPWM - PCA9685
private:
	unsigned int msRestLastWrite; /// only for timer, last time (in millis() of write Rest to eeprom)
	bool bNeeRestWrite; /// only for timer, = true when need write Rest to eeprom
	unsigned int mBeginRest; /// only for timer, 
	bool bNeedPinMode; /// for Ch==cDig, but not for Ch==cGpio, Ch==cPwm
    //---------------------------------------------------------------------------
	int GetRestEepromAddress()
	{
			/// first 100 bytes for clock alarm.
		if (Ch==cGpio) /// gpio
			return 100+sizeof(Rest)*iCh; /// Gpio<50
		if (Ch==cDig) ///pwm
			return 100 + sizeof(Rest)*(50 + iCh); /// pwm<50
		if (Ch == cPwm) ///pwm
			return 100 + sizeof(Rest)*(100 + iCh); /// pwm<50
		if (Ch == cAnlg) ///pwm
			return 100 + sizeof(Rest)*(150 + iCh); /// pwm<50
		return -1;
	}
    //---------------------------------------------------------------------------
	bool WriteRest(bool bNeedWrite=false) /// return true - write Rest to eeprom, else return false
	{
		if (Type != tTimer || !bNeeRestWrite)
			return false;
		//_TF_
		//	_TFl(bNeeRestWrite);
		unsigned int m = millis();
		//_TFl(m);
		//_TFl(msRestLastWrite);
		if (bNeedWrite || m < msRestLastWrite || m - msRestLastWrite > (10*60* 1000)) /// >10 min
		{
		//	_TP_
			GetTimeDo(2);
			if(!Eeprom.CheckI2C())
				return false;
			int EEPROMAddress = GetRestEepromAddress();
			if(EEPROMAddress<0)
			{
			//	_TP_
				return false;
			}
			float v;
			GetTimeDo(2);
			Eeprom.Read(EEPROMAddress, v);
			bNeeRestWrite=false;
			msRestLastWrite=m;
			if(v!= Rest)
			{
				//_TP_
				GetTimeDo(2);
				if(Eeprom.Write(EEPROMAddress, Rest)== EEPROMAddress+sizeof(Rest))
				{
		//			_TP_
					return true;
				}
			}
			//_TP_
		}
	//	_TP_
		return false;
	}
	//---------------------------------------------------------------------------
	void SetRest(float Rest_, bool bNeedWrite = false)
	{
		_TF_;
		_TFl(Rest_);
		if(Rest_<0)
			Rest=0;
		else
			Rest = Rest_;
		bNeeRestWrite=true;
		WriteRest(bNeedWrite);
	}
	//---------------------------------------------------------------------------
	ERegime Str2Regime(String S)
	{
		if (S == "Auto")
		{
		//	_TC("*********** ret rAuto")
			return rAuto;
		}
		if (S == "On")
			return rOn;
		if (S == "Off")
			return rOff;
		//if (S == "Temp")
		//{
		//	_TC("*********** ret rTemp")
		//		return rTemp;
		//}
	//	_TEr_
//			_TC("*********** ret rErr")
		return rOff;
	}
	//---------------------------------------------------------------------------
	String Regime2Str(ERegime R)
	{
		if (R == rAuto)
		{
	//		_TC("*********** ret Auto")
			return "Auto";
		}
		if (R == rOn)
			return "On";
		if (R == rOff)
			return "Off";
		//if (R == rTemp)
		//{
		//			_TC("*********** ret Temp")
		//				return "Temp";
		//}
	//	_TC("!!!!!!!!!!! ret Err")
		_TEr_
		return "Off";
	}
	////---------------------------------------------------------------------------
	bool SendValueCh(float Value)
		/// 0<=Value<=1
		/// return !err
	{
		MDelay(0);
		VNow = Value;
		//if (Ch == cDig)
		//{	_TFl(Value)
		//}
		//static float V = 0;
		//if(V!=Value)
		//{
		//	_TL;
		//	_TFl(Value);
		//	_TL;
		//	V = Value;
		//}
		if (Invert)
		{
					//if (Ch == cDig)
					//{	_TC("Invert")
					//}
					Value=1-Value;
		}
		if (Ch == cGpio)
		{
			float GPIOMax = powf(2, GpioBit) - 1;
			uint32_t V = GPIOMax*Value;
			if (iChGpio>7 && V == 0)
				V = 1;
			ledcWrite(iChGpio, V);
		}
		else if (Ch == cDig)
		{
			byte b;
			if (Value <= 0.4)
				b = LOW;
			else if (Value >= 0.6)
				b = HIGH;
			if (bNeedPinMode)
			{
				pinMode(iCh, OUTPUT);
				digitalWrite(iCh, b);
				bNeedPinMode = false;
			}
			else
				digitalWrite(iCh, b);
		}
		else if (Ch == cPwm)
		{
			//_TFl(iCh);
			//_TFl(Value);
			PWMDriver.setPWM(iCh, 0, PWMMax*Value);
		}
		else if (Ch = cAnlg)
			dacWrite(iCh, 255*Value);
		else
			return false;
		return true;
	}
	//---------------------------------------------------------------------------
public:
	short Index;
	ERegime Regime; 
	EType Type;
	String Name;
//	byte Color[3];
	//int PWM; /// PWM>=0 || GPIO>=0
	//int GPIO; /// PWM>=0 || GPIO>=0
	ECh Ch=cNone; /// Chanel type
	signed char iCh; /// index of chanel; 0<=_<=255 // number at D_, G_, P_ =GPIO, pin, PWM chanel
	signed char iChGpio; /// need for ledcAttachPin(iCh, iGhGpio);
	signed char iGr; /// index group of this chanel. -1 -nt group
	unsigned long Color;
	float Lm; /// for Type=tLED
	float Lux; /// for Type=tLED
	float W; /// for Type=tLED
	bool Invert;
	//static Adafruit_PWMServoDriver* PWMDriver;
	static const unsigned short PWMMax = 4095;
	byte GpioBit; /// resolution 1-16;
	//static const unsigned short GPIOMax = 8;//1023; //65535; // 1023;
//	ERegime Regime; /// 0-off, 1-on, 2-auto
	long YE; /// ms/y.e.
	String Dimension; /// ="ml", ...
//	todo: const static unsigned short PWMMax = 4095;

//	float VManual; /// for Type=Manual /// 0<= _ <=1 
//	float VEmergy; /// for Alarm /// 0<= _ <=1
	float VAuto; /// current value for auto regimr. When we set VManual, we here we remember auto value;
	float VMin; /// 0<= _ <= 1 /// for Type=tFan
	float VMax; /// 0<= _ <= 1 /// for Type=tFan
	float VNow; /// here remember current value (for return to json), only read, write useless, 0<= _ <= 1 

	float VManual; /// 0<= _ <= 1 if(=-1) off
	unsigned long TManualOffMs; /// millis when Vtemp need off

	float Rest; /// only for timer pump. temporary value, save/load to/from eeprom

	//---------------------------------------------------------------------------
	void Initial(short Index_)
	{
		iChGpio = -1;
		iCh = -1;
		iGr = -1;
		Index=Index_;
		bNeedPinMode=false;
		GpioBit=16;
		Type=tNone;
		Name="NamePWM";
		Color = 0;
		Lm=0;
		Lux=0;
		W=0;
		Invert=false;
		VManual=0; /// 0<= _ <=1 || =-1 - off
		VAuto = 0;
		VMin=0;  /// 0<= _ <=1
		VMax=1;  /// 0<= _ <=1
		VNow=-1;
		Regime = rOff;
		YE = -1;
		Dimension = "ml";

		VManual =-1;
		TManualOffMs=0;
		Rest = -1;

		msRestLastWrite=0;
		bNeeRestWrite=false;
		mBeginRest = UnsignedIntMax; /// max value
	}
	//---------------------------------------------------------------------------
	MPWMChanel(short Index_)
	{
		Initial(Index_);
	}
	//---------------------------------------------------------------------------
	~MPWMChanel()
	{
		if (Ch == cGpio && iChGpio >= 0 && iChGpio<LChGpio.CountGet())
		{
			LChGpio[iChGpio].Pin = -1;
		}
	}
	//---------------------------------------------------------------------------
	MPWMChanel(int Index_)
	{
		Initial(Index_);
		Name="NamePWM_"+String(Index);
	}
	//---------------------------------------------------------------------------
	void SetCh(ECh Ch_, signed char iCh_, signed char iChGpio_=-1, signed char GpioBit_=16) /// iChGpio_, GpioBit_,  only for gpio, not P_ or D_
		/// iCh - pin
		/// iChGpio - chanel of gpio ...=0-15
		/// iGpioBit - resolution of gpio ...=8-16
	{
		//_TF_
		if (Type==tTimer && (Ch!=Ch_ || iCh!=iCh_))
		{
			int EepromAddress = GetRestEepromAddress();
			if (EepromAddress>=0)
				WriteRest(true);
		}
		_TFl(Ch_);
		_TFl(iCh_);
		_TFl(iChGpio_);
		_TFl(GpioBit_);
		if (Ch==cGpio && 0<= iChGpio && iChGpio < LChGpio.CountGet() && LChGpio[iChGpio].PWMChanel!=this)
		{
			iCh = -1;
			iChGpio = -1;
			GpioBit = -1;
		}
		if (iCh_ >= 0 && Ch_ == cAnlg && iCh_ != 25 && iCh_ != 26) /// test Ch_
			return;
		if (iCh_ >= 0 && (Ch_ == cGpio || Ch_==cDig)
					&& iCh_ != 33 && iCh_ != 32 && iCh_ != 25 && iCh_ != 26 && iCh_ != 27 && iCh_ != 14
					&& iCh_ != 12 && iCh_ != 13 && iCh_ != 19 && iCh_ != 18 && iCh_ != 5 && iCh_ != 17 
					&& iCh_ != 16 && iCh_ != 4 && iCh_ != 2 && iCh_ != 15)
			return;
		_TP_
		if (Ch_ == cGpio)
		{
			_TP_;
			if (GpioBit_>0 && GpioBit_ < 8)
				GpioBit_ = 8;
			if(16<GpioBit_)
				GpioBit_ = 16;
			_TP_;
			if(LChGpio.CountGet()<iChGpio_)
				return;		
			_TP_
			if (0 <= iChGpio_ && iChGpio_ < LChGpio.CountGet())
			{
				_TP_
				if(iChGpio_==8)
				{
					_TFl(LChGpio[iChGpio_ + 1].Pin);
					_TFl(LChGpio[iChGpio_ + 1].Pin);
					_TFl(LChGpio[iChGpio_ - 1].Bit);
					_TFl(LChGpio[iChGpio_ - 1].Bit);
				}
				_TP_;
				if (iChGpio_ != iChGpio && LChGpio[iChGpio_].Pin != -1)  ///chanel busy
				{			
					return;
				}
				_TP_;
				if (iChGpio_ % 2 == 0 && LChGpio[iChGpio_ + 1].Pin != -1 && LChGpio[iChGpio_ + 1].Bit != GpioBit_)  /// 2 chanel per 1 timer. this timer have other resolution
					return;
				_TP_;
				if (iChGpio_ % 2 == 1 && LChGpio[iChGpio_ - 1].Pin != -1 && LChGpio[iChGpio_ - 1].Bit != GpioBit_)  /// 2 chanel per 1 timer. this timer have other resolution
					return;
				_TP_;
			}

		}
		_TP_
		if (Ch != Ch_ && Ch_ == cDig)
			bNeedPinMode = true;
		if (iCh >= 0 && Ch == cGpio)
		{
			for (int i = LChGpio.CountGet() - 1; i >= 0; i--) /// delete old
				if (LChGpio[i].Pin == iCh)
				{
					LChGpio[i].Pin = -1;
				}
		}
		_TP_
		iChGpio = -1; /// chanel
		iCh = -1; /// pin

		if (iCh>0 && Ch == cGpio)
			for (int i = LChGpio.CountGet() - 1; i >= 0; i--) /// delete current gpio
				if (LChGpio[i].PWMChanel == this) /// alredy exist
				{
					_TC("!!!!! ledcDetachPin(.) iCh=" + String(iCh));
					ledcDetachPin(iCh);
					LChGpio[i].Pin = -1;
					LChGpio[i].Bit = -1;
					LChGpio[i].PWMChanel = NULL;
					iCh = -1;
					break;
				}
		if (iCh_ >= 0 && Ch_ == cGpio)
		{
			if (iChGpio_ >= 0)
			{
				LChGpio[iChGpio_].Pin = iCh_;
				LChGpio[iChGpio_].Bit = GpioBit_;
				iChGpio = iChGpio_;
				iCh = iCh_;
				GpioBit = GpioBit_;
				LChGpio[iChGpio_].PWMChanel = this;
				ledcSetup(iChGpio, 1E8, GpioBit_);
				ledcAttachPin(iCh_, iChGpio);
			}
			else
			{
				_TP_;
				for (int i = 0; i < LChGpio.CountGet(); i++)
				{
					_TFl(i);
					_TFl(LChGpio[i].Pin);
					if (LChGpio[i].Pin < 0) /// set in free place
					{
						_TFl(i)
						if (i % 2 == 0 && (LChGpio[i + 1].Pin>0 && LChGpio[i + 1].Bit != GpioBit_)) /// 2 chanel per 1 timer. this timer have other resolution
							continue;
						_TP_
						if (i % 2 == 1 && (LChGpio[i - 1].Pin>0 && LChGpio[i - 1].Bit != GpioBit_))/// 2 chanel per 1 timer. this timer have other resolution
							continue;
						_TP_
						LChGpio[i].Pin = iCh_;
						LChGpio[i].Bit = GpioBit_;
						iChGpio = i;
						iCh = iCh_;
						LChGpio[i].PWMChanel = this;
						//_TFl(i)
						//	_TFl(iChGpio)
						//	_TFl(iCh_)
						//	_TFl(GpioBit)
						//	_TFl(GpioFreq)
						ledcSetup(iChGpio, 1E8, GpioBit_);
						_TP_
							ledcAttachPin(iCh_, iChGpio);
						GpioBit = GpioBit_;
						_TP_
							break;
					}
				}
			}
			_TFl(iChGpio);
			_TFl(iCh);
			_TFl(GpioBit);
		}
		else
			iCh = iCh_;
		Ch = Ch_;
		if (Type == tTimer && (Ch != Ch_ || iCh != iCh_))
		{
			ReadRest();
		}
	}
	//---------------------------------------------------------------------------
	void SetType(EType Type_)
	{
		if (Type!= Type_ && Type_ == tTimer)
		{
			Type = Type_;
			ReadRest();
		}
		else
			Type = Type_;
	}
	//---------------------------------------------------------------------------
	void Setup()
	{
		SetCh(Ch, iCh);
	}
	//---------------------------------------------------------------------------
	void ReadRest()
	{
		int EepromAddress = GetRestEepromAddress();
		if (EepromAddress < 0 || !Eeprom.Read(EepromAddress, Rest))
			Rest = -1;
	}
	//---------------------------------------------------------------------------
	void Loop()
	{
		if (VManual > 0 && TManualOffMs <= millis()) /// VTemp - off
		{
			//	_TL;
			//	_TC("TempOff");
			SetValueManual(-1, -1);
		}
		else if (Regime == rOn)
			SendOn();
		else if (Regime == rOff)
			SendOff();
		else
			SendValue(); /// ???
		if(Type == tTimer)
			WriteRest();
	}
	//---------------------------------------------------------------------------
	String GetGPIO_PWM()
	{
	//_TFB_
		if(iCh>=0)
		{
			if (Ch == cGpio && GpioBit>=0)
				return "G" + String(iCh) + "|" + String(iChGpio) + "|" + String(GpioBit); ///Pin|chanel|resolution
			if (Ch == cDig)
				return "D" + String(iCh);
			if (Ch == cPwm)
				return "P" + String(iCh);
			if (Ch == cAnlg)
				return "A" + String(iCh);
		}
		return "-";
	//	_TFE_
	};
	//---------------------------------------------------------------------------
	bool SetRegime(ERegime Regime_)
		/// return !err
	{
		if (Regime_ == rAuto && Regime_ != Regime)
		{
			Regime = Regime_;
			return SendOff();
		}
		Regime = Regime_;
		if (Regime == rOn)
			return SendOn();
		if (Regime == rOff)
			return SendOff();
		return false;
	}
	//---------------------------------------------------------------------------
	bool SendValue() 
	{
		float Value;
		if (VManual >= 0 && millis() < TManualOffMs) /// now VTemp
			Value = VManual;
		else
			Value = VAuto;
		float Rest_=-1;
		if (Type == tTimer && Rest > 0) /// will calc Rest in Timer
		{
			if (Value == VMax && mBeginRest == UnsignedIntMax)  /// dosing off->on
			{
			//	_TC("dosing off->on")
				mBeginRest=millis();
			}
			else if (Value == VMin && mBeginRest != UnsignedIntMax)  /// dosing on->off
			{
			//	_TC("dosing on->off")
				unsigned int m = millis()-mBeginRest;
				if (m > 0)
				{
					if (YE > 0) /// ml
						Rest_= Rest - float(m) / YE;
					else /// ms
						Rest_=Rest - m;
					if(Rest_<0)
						Rest_=0;
				}
				mBeginRest = UnsignedIntMax;
			}
			else if (Value == VMax && mBeginRest != UnsignedIntMax)  /// dosing on->on
			{
				//_TC("dosing on->on")
				unsigned int m0 = millis();
				unsigned int m = m0 - mBeginRest;
				if (m >= 1000) /// refresh Rest
				{
					if (YE > 0) /// ml
						Rest_ = Rest - float(m) / YE;
					else /// ms
						Rest_ = Rest - m;
					if (Rest_<0)
						Rest_ = 0;
					mBeginRest=m0;
				}
			}
		}
		if (Value < 0)
		{
_TEr_F;
		}
		bool r=false;
		if (Ch!=cNone)
			r=SendValueCh(Value);
		if(Rest_>=0) /// RestSet after send because need time for save to eeprom
			SetRest(Rest_);
		return r;
	}
	//---------------------------------------------------------------------------
	bool SendValueAuto(float Value)
		/// 0<=Value<=1
		/// return !err
	{
		if (Value < 0)
		{
				_TEr_
		//		_TFl(Value)
		}
		VAuto=Value;
		return SendValue();
	}
	//---------------------------------------------------------------------------
	bool SendOn() /// for Timer
		/// return !err
	{
		//_TF_;
		return SendValueAuto(VMax);
	}
	//---------------------------------------------------------------------------
	bool SendOff() /// for Timer
		/// return !err
	{
		//_TF_;
		return SendValueAuto(VMin);
	}
	//---------------------------------------------------------------------------
	bool SetValueManual(float VManual_, unsigned long TManualOffMs_) /// VManual<0 || ManualOffMs_ <millis() - off manual
		/// return !err
	{
//_TL
//_TFB_
		_TFl(VManual_)
	//_TFl(TManualOffMs_)
		//VManual = VManual_;
		TManualOffMs = TManualOffMs_;
		if(VManual_>=0 && millis()<TManualOffMs_) /// VManual - on
		{
_TC("On")
			VManual = VManual_;
			TManualOffMs = TManualOffMs_;
		}
		else  /// VManual - off
		{
		//	_TC("Off")
				VManual = -1;
			TManualOffMs=0;
			VManual_ = -1;
		}
		VManual = VManual_;
		SendValue();
		return true;//SetRegime(rTemp);
	}
	//---------------------------------------------------------------------------
	void SetManualFromJson(MJsonLoad& JL)
	{
		float VManual_ = -1;
		unsigned long TManualOffMs_ = 0;

		while (JL.GetNext() && JL.Key != "}")
		{
			String sKM(JL.Key);
			//_TFl(sKM)
			if (sKM == "V")
			{
				VManual_ = JL.Value.toFloat(); //100;
				InsideSet(-1, VManual_, 1);
				if (VManual_ < 0)
					VManual_ = -1;
			//	_TFl(VManual_);
			}
			else if (sKM == "TOffMs")
			{
				TManualOffMs_ = millis() + JL.Value.toInt(); // it->value.as<unsigned long>();
		//		_TFl(millis());
		//		_TFl(TManualOffMs_);
			}
			else 
			{
				_TErFl(JL.Value);
				_TErFl(sKM);
			}
		}
		SetValueManual(VManual_, TManualOffMs_);
		//_TFE_
	}
	//---------------------------------------------------------------------------
	void SetFromJSon(MJsonLoad& JL) /// set all params consisting in JO to this
									 /// return !err
	{
		_TFB_
		static ERegime RegimeSave = rOff;
		static float CurrentValueSave=0;
		String Dimension_ = "@undef";
		long YE_=YE;
		float Rest_=Rest;
		bool bNeedSetRest=false;
		while (JL.GetNext() && JL.Key != "}")
		{
			String sK(JL.Key);
		//	_TFl(sK)
			if (sK == "Regime")
			{
				ERegime Reg = Str2Regime(JL.Value);
				SetRegime(Reg);
			}
			else if (sK == "Name")
				Name = JL.Value;
			else if (sK == "Color")
				Color = JL.Value.toInt(); // it->value.as<unsigned long>();
			else if (sK == "GPIO_PWM")
			{
				String S0 = JL.Value; // it->value.as<String>();
				String S = S0.substring(0, 1);
				S0 = S0.substring(1, S0.length());
				_TFl(S)
				if (S == "G") /// GPIO analog
				{
					int i0=S0.indexOf("|");
					if(i0>=0)
				  {
						signed char Pin_= S0.substring(0, i0).toInt();
						S0 = S0.substring(i0 + 1, S0.length());
						i0 = S0.indexOf("|");
						if (i0 >= 0)
							SetCh(cGpio, Pin_, S0.substring(0, i0).toInt(), S0.substring(i0 + 1, S0.length()).toInt());
						else
							SetCh(cGpio, S0.toInt());
					}
					else
						SetCh(cGpio, S0.toInt());
				}
				else if (S == "D") /// GPIO digit
					SetCh(cDig, S0.toInt());
				else if (S == "P") /// PWM
					SetCh(cPwm, S0.toInt());
				else if (S == "A") /// Analog from DAC 
					SetCh(cAnlg, S0.toInt());
				else
					SetCh(cNone, 0);
			}
			else if (sK == "Lm")
				Lm = Str2Float(JL.Value); /// bug-fix String:toFloat() not recognize format 1.2e4 or 1.2E4
			else if (sK == "Lux")
				Lux = Str2Float(JL.Value);
			else if (sK == "W")
				W = Str2Float(JL.Value);
			else if (sK == "Gr")
				iGr = Str2Float(JL.Value);
			else if (sK == "VMin")
			{
				VMin = JL.Value.toFloat(); // it->value.as<float>();// / 100.;
				InsideSet(0, VMin, 1);
				SetRegime(Regime);
			}
			else if (sK == "VMax")
			{
				VMax = JL.Value.toFloat();  // / 100.;
				InsideSet(0, VMax, 1);
				SetRegime(Regime);
			}
			else if (sK == "Invert")
			{
				Invert = JL.ValueAsBool(); //it->value.as<bool>();
				//if (Ch == cDig)
				//{
				//	_TL;
				//	_TL;
				//	_TFl(iCh);
				//	_TFl(JL.ValueAsBool());
				//	_TFl(Invert);
				//	_TL;
				//}
				SetRegime(Regime);
			}
			else if (sK == "VManual")
			{
				if(JL.Value!="{") /// temp, will can delete
					continue;
				SetManualFromJson(JL);
			}
			else if(sK=="VEmergy"){}
			else if (sK == "YE")
				YE = JL.Value.toInt();
			else if (sK == "Dimension")
				Dimension_ = JL.Value;
			else if (sK == "Rest")
			{
		//		_TC("********************");
				Rest_ = JL.Value.toFloat();
				bNeedSetRest=true;
		//		_TFl(Rest)
		//		_TFl(Rest_)
			}
			else
			{
				_TErFl(JL.Key);
				_TErFl(JL.Value);
			}
			MDelay(5); /// !!! bug, else (<5) low level relay (on GPIO) is pic off-on-off at restart esp
		}
		if(Type==tTimer)
		{
			if(Dimension_ != "@undef")
			{
				if (Rest_ >= 0 && Dimension != Dimension_ && !bNeedSetRest) /// Dimension - changed and Rest not change
				{
					if (Dimension == "" && Dimension_ != "" && YE_!=0)  ///ms -> ml
						SetRest(Rest_/YE_);
					else if (Dimension != "" && Dimension_ == "")  /// ml -> ms
						SetRest(Rest_ * YE_);
				}
				Dimension = Dimension_;
			}
			if(bNeedSetRest) /// not use Rest_!=Rest, Rest can change at set VManual
			{
				//_TC("********************");
			//	_TFl(Rest)
			//	_TFl(Rest_)
				SetRest(Rest_, true);
			}
		}
	//	if(bNeedSetup)
		//	Setup();
		//	_TFE_
	}
	//---------------------------------------------------------------------------
	void GetToJSon(JsonObject& JOP, MJsonSave& JS, bool bAll = false, int RegimeSave = 1)
		/// not use RegimeSave = 0 - for save to file, 1 - save for http
		/// return all params consisting in JOP to JO
																	   /// return !err
	{
		//_TFB_
		String SS;
		JOP.printTo(SS);
			//_TC(SS)
//			_TL
//		_TFl(bAll)
//_TFl(RegimeSave)

		if (!bAll && JOP.size() < 1)
			_TEr_FR();
		String sK;
		//int Size = 0;
		for (JsonObject::iterator it = JOP.begin(); bAll || it != JOP.end(); ++it)
		{
			int Size=JS.Size;
			if (!bAll)
				sK = it->key;
			bAll = (bAll || sK == "All");
			if (bAll || sK == "Regime")
				JS.Add("Regime", Regime2Str(Regime));
			//if (bAll || sK == "Type")
			//	JO["Type"] = Type2Str(Type);
			if (bAll || sK == "Name")
{
//	_TFl(Name)
				JS.Add("Name", Name);
}
			if (bAll || sK == "Color")
			{
			//	_TFl(Color)
					JS.Add("Color", (unsigned long)Color);
			}
			if (bAll || sK == "GPIO_PWM")
			{
				JS.Add("GPIO_PWM", GetGPIO_PWM());
			}
			if (Type == tLED && (bAll || sK == "Lm"))
				JS.Add("Lm", Lm);
			if (Type == tLED && (bAll || sK == "Lux"))
				JS.Add("Lux", Lux);
			if (Type == tLED && (bAll || sK == "W"))
				JS.Add("W", W);
			if (Type == tLED && (bAll || sK == "Gr"))
				JS.Add("Gr", iGr);
			if (bAll || sK == "Invert")
			{
				JS.Add("Invert", int(Invert));
				//if (Ch == cDig)
				//{
				//	_TL;
				//	_TL;
				//	_TFl(Invert);
				//	_TL;
				//}
			}
		//	if (bAll || sK == "VManual")
	//			JO["VManual"] = int(1000.*VManual*100. / PWMMax + 0.5) / 1000.;
		//		JS.Add("VManual", VManual*100.);
	//		if (bAll || sK == "VEmergy")
	////			JO["VEmergy"] = int(1000.*VEmergy*100. / PWMMax + 0.5) / 1000.;
	//			JO["VEmergy"] = VEmergy*100.;
			if (Type!=tLED && (bAll || sK == "VMin"))
			{
				JS.Add("VMin", VMin);
			}
			if (Type != tLED && (bAll || sK == "VMax"))
	//			JO["VMax"] = int(1000.*VMax*100. / PWMMax + 0.5) / 1000.;
				JS.Add("VMax", VMax);
//			if (bAll || sK == "Regime")
	//			JS.Add("Regime", Regime2Str(Regime));
			if (bAll || sK == "YE")
				JS.Add("YE", YE);
			if (bAll || sK == "Dimension")
				JS.Add("Dimension", Dimension);
			if ((bAll && RegimeSave == 1) || sK == "VNow") /// current value
				JS.Add("VNow", int(1000.*VNow*100.) / 1000.);
			if ((bAll && RegimeSave == 1) || sK == "Rest")
			{
//_TC("*********************");
		//		_TFl(Rest)
//					JS.Add("Rest", FloatToStr(Rest, 2)); // it->value.as<int>(), 0);
				JS.Add("Rest", int(Rest)); // it->value.as<int>(), 0);
			}
//				JO["AlarmValue"] = AlarmValue;
		//	if (bAll || sK == "ID")
			//	JO["ID"] = ID;
			if (!bAll && JS.Size == Size)
			{
				String sJOP;
				JOP.printTo(sJOP);
				_TErFl(sJOP);
			}
			MDelay(0);
			if (bAll)
				break;
		}
		//String S;
		//JO.prettyPrintTo(S);
		//_TFl(S)
			//_TFE_
	}
	//---------------------------------------------------------------------------
};

//Adafruit_PWMServoDriver* MPWMChanel::PWMDriver=NULL;

//---------------------------------------------------------------------------
class MListPWMChanel : public MListSimple<MPWMChanel*>
{
public:
	const int AddressPCA9685 = 0x40; /// PWM
	int Frequency;// = 1000; /// if(_<1) than pwm disabled
	float WMax;

	MPWMChanel::EType Type;
	//---------------------------------------------------------------------------
	MListPWMChanel(MPWMChanel::EType Type_)
	{;
		//MPWMChanel::PWMDriver = &PWMDriver;
		Frequency = 1000; /// if(_<1) than pwm disabled
		WMax=0;
		Type = Type_;
	}
	//---------------------------------------------------------------------------
	void Setup() /// call from LoadParamFromJson(.)
	{
		PWMDriver.reset();
		PWMDriver.begin();
		PWMDriver.setPWMFreq(Frequency);
		//PWMDriver.setPWM(2, 0, (4096 - 1000));
		//PWMDriver.setPWM(3, 0, 1000);
	}
	//---------------------------------------------------------------------------
	void Loop()
	{
		for(int i= CountGet()-1; i>=0; i--)
			Data[i]->Loop();
	}
	//---------------------------------------------------------------------------
	bool CheckPWMDriver()
		/// return true - found pwm, false - not found pwm
	{
		Wire.beginTransmission(AddressPCA9685);
		if (Wire.endTransmission() == 0)
			return true;
		return false;
	}
	//---------------------------------------------------------------------------
	void CheckPWM()
	{
		//_TF_
		for (int i1 = 0; i1 < CountGet(); i1++) /// find repeat id
			for (int i2 = i1 + 1; i2 < CountGet(); i2++)
				if (Data[i1]->Ch!=MPWMChanel::cNone && Data[i1]->Ch == Data[i2]->Ch && Data[i1]->iCh == Data[i2]->iCh)
					Data[i2]->Ch = MPWMChanel::cNone;
	//			else if (Data[i1]->GPIO >= 0 && Data[i1]->GPIO == Data[i2]->GPIO)
		//			Data[i2]->GPIO = -1;
	}
	//---------------------------------------------------------------------------
	void SetFromJSon(MJsonLoad& JL) /// set all params consisting in JO to this
									 /// return !err
	{
		_TFB_;
		//String S;
	//JO.prettyPrintTo(S);
	//_TFl(S)

//	ListFromJson(*this, JO);
		int Count = -1;
		bool bGroup = (JL.FindKey("Group")!="");

		_TFl(bGroup)
		while (JL.GetNext() && JL.Key != "}")
		{
			String sK(JL.Key);
_TFl(sK)
			_TFl(JL.Value)
			if (sK == "Count")
				Count = JL.Value.toInt();
			else if (sK == "Group")
				bGroup = true;
			//(JL.Value.toInt()!=0);
			else if (sK == "Data")
				ListFromJson(*this, JL, Count, bGroup);
			else if (sK == "Frequency")
				Frequency = Max(JL.Value.toInt(), 0); // it->value.as<int>(), 0);
			else if (sK == "WMax")
				WMax = Max(JL.Value.toInt(), 0);
			//	continue;
			//else if (sK == "Count")
			//{
			//	Count = JL.Value.toInt();
			//	for (int i = 0; i<CountGet(); i++)
			//		Data[i]->Type = Type;
			//}
			else //if (sK != "Data")
			{
				_TErFl(sK);
				_TErFl(JL.Value);// it->value.as<String>());
			}
			MDelay(0);
		}
		CheckPWM();
		SetType(Type);
		
		_TFE_
		//CheckID();
	}
	//---------------------------------------------------------------------------
	void GetToJSon(JsonObject& JOP, MJsonSave& JS, bool bAll = false, int RegimeSave=1)
			/// return all params consisting in JA to JO
			/// return !err
			/// RegimeSave = 0 - for save to file, 1 - save for http
	{
		//_TFB_
		int Size = 0;
		String sK;
		bool bGroup=false;
	//	bool bAll_=bAll; /// for send to MList
	//	bool bGroup = false;
		for (JsonObject::iterator it = JOP.begin(); bAll || it != JOP.end(); ++it)
		{
			int Size=JS.Size;
		//	_TFl(Size)
			if (!bAll)
				sK = it->key;
			bAll = (bAll || sK == "All");
		//	_TFl(sK)
			if (sK == "ShowTab") 
			{
				bool b=false;
				//_TFl(CountGet())
				for (int i = CountGet() - 1; i >= 0; i--)
					if ( (Data[i]->Regime== MPWMChanel::rAuto) // || Data[i]->Regime == MPWMChanel::rTemp) 
								&& Data[i]->Ch!=MPWMChanel::cNone)//(Data[i]->PWM>=0 || Data[i]->GPIO>=0) )
					{
					//	_TFl(i)
						b=true;
						break;
					}
			//	_TFl(b)
			//	_TL
				JS.Add("ShowTab", b);
			}
			if ((bAll && RegimeSave == 1) || sK == "PCA9685")
			{
				if (CheckPWMDriver())
					JS.Add("PCA9685", "Found");
				else
					JS.Add("PCA9685", "Not found");
			}
			if (bAll || sK == "Frequency")
				JS.Add("Frequency", Frequency);
			//if (sK == "Group")
			//	bGroup=(it->value.as<int>() != 0);
			if (bAll || sK == "WMax")
				JS.Add("WMax", WMax);
			if (sK == "Group")
				bGroup = true;
			if (!bAll && JS.Size == Size && sK != "Data" && sK != "Count")
			{
				String sJA;
				JOP.printTo(sJA);
				_TErFl(sJA);
				_TErFl(sK)
			}
			MDelay(0);
			if (bAll)
				break;
		}
		/// bAll here calculated
	//	_TFl(bAll)
		//if(bGroup)
		//{
		//	int CountGr = 0;
		//	MListSimple<signed char> LC;
		//	for (int i = CountGet()-1; i >=0 ; i--)
		//		if (Data[i]->iGr >= 0)
		//		{
		//			bool bFind = false;
		//			for(int i0=LC.CountGet()-1; i0>=0; i0--)
		//				if(LC[i0]== Data[i]->iGr)
		//				{
		//					bFind = true;
		//					break;
		//				}
		//			if (!bFind)
		//				CountGr++;
		//		}
		//		else
		//			CountGr++;
		//	JS.Add("Count", CountGr);
		//}
		//else
			ListToJson(*this, JOP, JS, bAll, RegimeSave, bGroup);

		//String S;
		//JO.prettyPrintTo(S);
		//_TFl(S)

	//	_TFE_
	}
	//---------------------------------------------------------------------------
	void SetType(MPWMChanel::EType Type)
	{
		for (int i = CountGet()-1; i >= 0; i--)
			Data[i]->SetType(Type);
	};
	//---------------------------------------------------------------------------
	void ReadRest()
	{
		for (int i = CountGet() - 1; i >= 0; i--)
			Data[i]->ReadRest();
	}
};
//---------------------------------------------------------------------------
#endif  
