#include <Arduino.h>
#include <esp32-hal-ledc.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "pushButton.h"
#include <EveryTimer.h>

// https://javl.github.io/image2cpp/
// 'sunheat', 128x64px
const unsigned char epd_bitmap_sunheat [] PROGMEM = {	0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xef, 0x7b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xbf, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 
	0xf6, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x7f, 0xff, 0xe3, 0x9f, 0xff, 0xff, 0xff, 0xff, 
	0xf8, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x7f, 0xff, 0xe3, 0xf9, 0xcd, 0x9f, 0xff, 0xff, 
	0xfb, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xf0, 0xf9, 0xcc, 0x0f, 0xff, 0xff, 
	0xf7, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xf8, 0x39, 0xcc, 0xc7, 0xff, 0xff, 
	0x97, 0xf0, 0x7c, 0x3f, 0xff, 0xff, 0xff, 0xf5, 0xff, 0xff, 0xff, 0x19, 0xcc, 0xe7, 0xff, 0xff, 
	0xf3, 0xf7, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xe7, 0x99, 0xcc, 0xe7, 0xff, 0xff, 
	0xf9, 0xe7, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xeb, 0xff, 0xff, 0xe3, 0x99, 0xcc, 0xe7, 0xff, 0xff, 
	0xf4, 0x17, 0xc3, 0xc3, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xf0, 0x18, 0x0c, 0xe7, 0xff, 0xff, 
	0xe7, 0xfb, 0x8f, 0xe1, 0xff, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xf8, 0x3c, 0x4c, 0xe7, 0xff, 0xff, 
	0xff, 0x7f, 0x8f, 0xf1, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0x7f, 0x8f, 0xf1, 0xff, 0xff, 0xff, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8f, 0xf1, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8f, 0xf1, 0xff, 0xff, 0xff, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8f, 0xf1, 0x9f, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8f, 0xf1, 0xff, 0xff, 0xfe, 0xbf, 0xff, 0xff, 0xf7, 0xdf, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8f, 0xf1, 0xff, 0xff, 0xfd, 0x7f, 0xff, 0xff, 0xe3, 0xcf, 0xff, 0xfc, 0xff, 0xff, 
	0xff, 0xff, 0x8f, 0xf1, 0xff, 0xff, 0xfd, 0x7f, 0xff, 0xff, 0xe3, 0xcf, 0xff, 0xfc, 0xff, 0xff, 
	0xff, 0xff, 0x8f, 0xf1, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xe3, 0xce, 0x3e, 0x18, 0x71, 0xe9, 
	0xff, 0xff, 0x8f, 0xf1, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xe3, 0x8c, 0x0c, 0x08, 0x60, 0xe1, 
	0xff, 0xff, 0x8e, 0xf1, 0x9f, 0xff, 0xf5, 0xff, 0xff, 0xff, 0xe0, 0x0c, 0xcf, 0xcc, 0xce, 0x67, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xff, 0xf7, 0xff, 0xff, 0x03, 0xe3, 0xc8, 0x0e, 0x0c, 0xc0, 0x67, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xff, 0xeb, 0xff, 0xfc, 0x00, 0xe3, 0xc8, 0x0c, 0x4c, 0xc0, 0x67, 
	0xff, 0xff, 0x8c, 0x71, 0x1f, 0xff, 0xef, 0xff, 0xf8, 0x00, 0x23, 0xcc, 0xf9, 0xcc, 0xcf, 0xe7, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xff, 0xd7, 0xff, 0xf0, 0x00, 0x03, 0xcc, 0x0c, 0x0c, 0x60, 0x67, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xff, 0xef, 0xff, 0xf0, 0x00, 0x17, 0xce, 0x1e, 0x4e, 0x71, 0xef, 
	0xff, 0xff, 0x8c, 0x71, 0x01, 0xff, 0xaf, 0xff, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xff, 0x9f, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xff, 0x5f, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xfe, 0xbf, 0xff, 0xf0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xfe, 0xbf, 0xff, 0xf0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xfd, 0x7f, 0x1f, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xfd, 0x7e, 0x0f, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x8c, 0x71, 0xff, 0xfe, 0xfc, 0x07, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xfe, 0x0c, 0x70, 0xff, 0xf9, 0xfc, 0x03, 0xf8, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xfc, 0x1c, 0x70, 0x3f, 0xf5, 0xfc, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xf8, 0x7c, 0x7c, 0x1f, 0xf7, 0xfc, 0x00, 0xfc, 0x0f, 0x80, 0x3f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xf0, 0xfc, 0x3e, 0x1f, 0xeb, 0xfc, 0x00, 0x78, 0x04, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xf1, 0xf0, 0x0f, 0x0f, 0xeb, 0xfc, 0x00, 0x10, 0x02, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xe1, 0xe0, 0x07, 0x8f, 0xd7, 0xfc, 0x00, 0x10, 0x02, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xe3, 0xc0, 0x03, 0x87, 0xef, 0xfc, 0x00, 0x10, 0x02, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xe3, 0x80, 0x03, 0xc7, 0xaf, 0xfc, 0x00, 0x10, 0x02, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xc3, 0x80, 0x01, 0xc7, 0x9f, 0xfc, 0x00, 0x10, 0x02, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xc3, 0x80, 0x01, 0xc7, 0x5f, 0xfe, 0x00, 0x18, 0x07, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xe3, 0x80, 0x03, 0xc6, 0xbf, 0xfe, 0x00, 0x7c, 0x0f, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xe3, 0x80, 0x03, 0xc6, 0xbf, 0xff, 0xff, 0xfe, 0x3f, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xe3, 0xc0, 0x03, 0x85, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xe1, 0xe0, 0x07, 0x8d, 0x7f, 0xff, 0xff, 0xfc, 0x07, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xf0, 0xf0, 0x1f, 0x0a, 0xff, 0xff, 0xff, 0xfc, 0x07, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xf8, 0x7f, 0xfe, 0x1b, 0xff, 0xff, 0xff, 0xf8, 0x07, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xf8, 0x3f, 0xfc, 0x35, 0xff, 0xff, 0xff, 0xf0, 0x07, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xfc, 0x0f, 0xf0, 0x77, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x00, 0x00, 0xeb, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xf0, 0xff, 0x87, 0xc7, 0xc3, 0xc1, 
	0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0x80, 0x03, 0xcf, 0x7f, 0x03, 0x03, 0x01, 0x81, 
	0xff, 0xff, 0xf0, 0x1f, 0xd7, 0xff, 0xff, 0xfe, 0x00, 0x03, 0xd9, 0xbe, 0x33, 0x31, 0x19, 0x9f, 
	0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xfe, 0x00, 0x03, 0xb4, 0xbf, 0xf3, 0x31, 0xf1, 0x83, 
	0xff, 0xff, 0xff, 0xff, 0xaf, 0xff, 0xff, 0xfe, 0x00, 0x03, 0xa7, 0xff, 0xe3, 0x39, 0xe3, 0x81, 
	0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x00, 0x07, 0xa6, 0xbf, 0xc7, 0x39, 0xc7, 0xf9, 
	0xff, 0xff, 0xff, 0xff, 0x5f, 0xff, 0xff, 0xff, 0x80, 0x0f, 0xb0, 0xbf, 0x8f, 0x31, 0x8f, 0xf8, 
	0xff, 0xff, 0xff, 0xfe, 0xbf, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xdf, 0x7f, 0x03, 0x13, 0x01, 0x19, 
	0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xfe, 0x03, 0x83, 0x01, 0x83, 
	0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 1040)
const int epd_bitmap_allArray_LEN = 1;
const unsigned char* epd_bitmap_allArray[1] = {
	epd_bitmap_sunheat
};

bool afterSplash;
//======================================================================================================================
#include <OneWire.h>
#include <DallasTemperature.h>

// GPIO where the DS18B20 is connected to
const int oneWireBus = 18; 
OneWire ds18x20[] = { 18, 5 };
const int oneWireCount = sizeof(ds18x20) / sizeof(OneWire);
DallasTemperature sensor[oneWireCount];

#define LED_ONOFF 2
#define BTN_PINONOFF    13
pushButton buttonONOFF(BTN_PINONOFF);

#define BTN_PINFUNCTION 12
pushButton buttonFn(BTN_PINFUNCTION);

#define BTN_PINUP       14
pushButton buttonUp(BTN_PINUP);

#define BTN_PINDOWN     27
pushButton buttonDown(BTN_PINDOWN);
//----------------------
EveryTimer timerHeart;
EveryTimer timerTemp;


const int ledPinFOut = 19; 
const int ledPinFIn = 15; 

// setting PWM properties
const int resolution = 8;    // using 8 bit resolution to control the brightness from 0 to 255.


enum stateN {OFF=0,IN,OUT,COOL,HEAT};
const char *stateLabels[]={"OFF","IN","OUT","COOL","HEAT"};

typedef struct {
  int onoff,old_onoff;
  int fn,old_fn;
  int upb,old_upb;
  int dnb,old_dnb;
  float roomTarget;
  float difTemp;
  float old_difTemp;
  int numDevices;
  DeviceAddress deviceAddress[2];
  float temperatures[2];
  stateN nState;
  int fanIN,old_fanIN;
  int fanOUT,old_fanOUT;
  int inPWM,outPWM;
}kbMini;

kbMini kbWork,old_kbWork;
//---------------------------------------------------------------------------
String getStateLabel(stateN st)
{
  return String(stateLabels[(int)st]);
}
//---------------------------------------------------------------------------
String getTempStr(float val,int symbol)
{
  char buff[10];
  sprintf(buff,"%.1f",val);

  String ret=String(buff);
  if(symbol)
  {
    ret+=String(((char)247))+String("C");
  }
  return ret;
}
//---------------------------------------------------------------------------
const int SCREEN_WIDTH = 128; // OLED display width, in pixels
const int SCREEN_HEIGHT = 64; // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//----------------------------------------------------------------------------------
void drawSplahsScreen() {
  display.clearDisplay();
 display.drawBitmap(0, 0, epd_bitmap_allArray[0], 128, 64, WHITE);
 display.display();
 delay(2000);
 afterSplash=true;
}
//------------------------------------------------------------------------------
void drawArrow(int x1, int y1,int wx,int wy,bool right)
{
    int Point3x,Point2x,Point1x;
    int Point3y,Point2y,Point1y;
    int pmXTriangle,pmXTail;
    
    Point1x=x1+wx/2;
    Point1y=y1;

    Point2x=Point1x;
    Point2y=y1+wy;

    Point3x=right?(x1+wx):x1;
    Point3y=y1+wy/2;

    display.fillTriangle(Point1x, Point1y, Point2x , Point2y, Point3x, Point3y, WHITE);
    
    pmXTriangle=Point1x;
    pmXTail=(right)?(Point1x-wx/2):(Point1x+wx/2);
    display.fillRect(min(pmXTriangle,pmXTail), Point3y-2, abs(pmXTriangle-pmXTail),7, SSD1306_WHITE );
}
//--------------------------------------------------------------------------------------
void drawHeart(int x,int y,int r)
{
 static int color=WHITE;
 if(kbWork.fanIN || kbWork.fanOUT)
  {
   display.fillCircle(x, y, r, color);
   color=(color==WHITE)?BLACK:WHITE;
   display.display();
  }
}
//--------------------------------------------------------------------------------------
#define stepY 13
#define p1x   32
#define p2x   96
#define p3x   SCREEN_WIDTH
#define pmx   64
#define pmdx  5

#define p1y   stepY
#define p1yd  (2.5*stepY) //(2.1*stepY)
#define p2y   3*stepY
#define p3y   SCREEN_HEIGHT


#define T1y   (1.65*stepY)
#define T2y   (3.5*stepY)
#define T3y   (2+3.8*stepY) //3.5
#define T3yU (2+3*stepY)
/*
         p1x    pmx    p2x    p3x
+--------+-------------+-------+
|PANEL °C|             |ROOM °C| stepY
+--------+   pmdx      +-------+ p1y
|            +--+--+           | piyd
| T1y           |              | stepY
+---------------+--------------+ p2y
|               |              | stepY
| T2y           | T3y          | stepY
+---------------+--------------+ p3y

*/
void drawFrame()
{
  String diffT;
  int16_t x1, y1;
  uint16_t width,height;
 
  display.clearDisplay();
  display.drawRect(0, 0,p3x, p3y,SSD1306_WHITE);

  display.drawLine(2*pmdx, p1y, p3x-2*pmdx, p1y,SSD1306_WHITE);

  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(2,2);
  display.println(String("Out")+String(((char)247))+String("C"));
  display.setCursor(p2x+2,2);
  display.println(String("In ")+String(((char)247))+String("C"));

  diffT=getTempStr(kbWork.difTemp,1);
  display.getTextBounds(diffT, 0, 0, &x1, &y1, &width, &height);
 
  display.setCursor(pmx-width/2,2);
  display.print(diffT);

  display.setTextSize(2);
  display.setCursor(2,T1y);
  display.print(getTempStr(kbWork.temperatures[0],0));
  display.setCursor(8+pmx,T1y);
  display.print(getTempStr(kbWork.temperatures[1],0));

  display.drawLine(0, p2y, p3x, p2y,SSD1306_WHITE);
//-----------------------------------------------------------
  display.setCursor(2,T2y);
  if(kbWork.fanIN || kbWork.fanOUT)
  {
    display.fillRect(0, p2y,pmx,2*stepY,SSD1306_WHITE);
    display.setTextColor(BLACK);
  }

  display.print(getStateLabel(kbWork.nState));
  
  if(kbWork.fanIN || kbWork.fanOUT)
  {
  display.setTextColor(WHITE);
  }
//------------------------------------------------------
  display.setTextSize(1);
  
  if(kbWork.nState==HEAT || kbWork.nState==COOL)
  {
    display.setCursor(20+pmx,T3y);
    display.print(getTempStr(kbWork.roomTarget,1));
    display.setCursor(2+pmx,T3yU);
    display.print("SET");
  }
     //Vertical
  display.drawLine(pmx, p1yd+2, pmx, p3y,SSD1306_WHITE);
  display.drawLine(p1x, 0, p1x, p1y,SSD1306_WHITE);
  display.drawLine(p2x, 0, p2x, p1y,SSD1306_WHITE);
  //------------------------------------------
 int lx=pmx-pmdx;
 int wx=2*pmdx+1;
 int hy=p1yd-p1y;

 if(kbWork.nState==OFF)
 {
   display.fillRect(lx,p1y,wx,hy,SSD1306_WHITE);
 }
 else if(kbWork.nState==OUT)
 {
   drawArrow(lx,p1y,wx,hy,false);
 }
 else if(kbWork.nState==IN)
 {
   drawArrow(lx,p1y,wx,hy,true);
 }
 else  if((kbWork.nState==HEAT) && (kbWork.fanIN==HIGH)) 
 {
   drawArrow(lx,p1y,wx,hy,true);
 }
 else  if((kbWork.nState==COOL) && (kbWork.fanOUT==HIGH)) //COOLas
 {
   drawArrow(lx,p1y,wx,hy,false);
 }
 else  if((kbWork.nState==HEAT) && (kbWork.fanIN==LOW) || (kbWork.nState==COOL) && (kbWork.fanOUT==LOW)) //COOLas
 {
  display.drawLine(pmx-pmdx, p1yd, pmx+pmdx, p1yd,SSD1306_WHITE);
  display.drawLine(pmx-pmdx, p1yd-4, pmx+pmdx, p1yd-4,SSD1306_WHITE);
  display.drawLine(pmx-pmdx, p1yd-8, pmx+pmdx, p1yd-8,SSD1306_WHITE);
  display.drawLine(pmx-pmdx, p1yd-12, pmx+pmdx, p1yd-12,SSD1306_WHITE);
  display.drawLine(pmx-pmdx, p1yd-16, pmx+pmdx, p1yd-16,SSD1306_WHITE);
  display.drawLine(pmx, p1yd, pmx, p1yd-16,SSD1306_WHITE);
 }
}
//--------------------------------------------------------------
void drawHeartT()
{
  drawHeart(pmx-7,p2y+7,5);
}
//------------------------------------------------------------------------------------
 void readSensorsT()
 {
 if (kbWork.numDevices > 0)
  {
    sensor[0].requestTemperatures();
    kbWork.temperatures[0]= sensor[0].getTempC(kbWork.deviceAddress[0]);
    sensor[1].requestTemperatures();
    kbWork.temperatures[1]= sensor[1].getTempC(kbWork.deviceAddress[1]);
    kbWork.old_difTemp=kbWork.difTemp;
    kbWork.difTemp=kbWork.temperatures[0]-kbWork.temperatures[1];
  }
 }
//--------------------------------------------------------------------------------------
void setup() {

  Serial.begin(115200);
   Serial.println(F("Starting ......."));
   memset(&kbWork,0,sizeof(kbMini));
   kbWork.roomTarget=23.5;
   
   kbWork.nState=OFF;


// configure LED PWM functionalitites
  
  // attach the channel to the GPIO to be controlled
  ledcAttach(ledPinFOut,  50, resolution);
  ledcAttach(ledPinFIn,  50, resolution);


  pinMode(LED_ONOFF, OUTPUT);

    if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
        Serial.println(F("SSD1306 allocation failed"));
        while(true);
    }
  
  sensor[0].setOneWire(&ds18x20[0]);
  sensor[0].begin();
  sensor[0].setWaitForConversion(false);
  sensor[0].getAddress(kbWork.deviceAddress[0], 0);

  sensor[1].setOneWire(&ds18x20[1]);
  sensor[1].begin();
  sensor[1].setWaitForConversion(false);
  sensor[1].getAddress(kbWork.deviceAddress[1], 1);

  kbWork.numDevices=2;
   drawSplahsScreen();

  timerHeart.Every(1000, drawHeartT);
  timerTemp.Every(4500,readSensorsT);
}
//------------------------------------------------------------------------------------
void readButtons()
{
  kbWork.onoff= buttonONOFF.retentionState();
  if(kbWork.onoff!=kbWork.old_onoff)
  {
     digitalWrite(LED_ONOFF, kbWork.onoff);
     kbWork.old_onoff=kbWork.onoff;
     kbWork.nState=(kbWork.onoff==HIGH)?IN:OFF;
     return;
  }
  if(kbWork.nState==OFF)
  {
    return;
  }
  kbWork.fn=buttonFn.retentionState();
  if(kbWork.fn!=kbWork.old_fn)
   {
     kbWork.old_fn=kbWork.fn;
     kbWork.nState=(kbWork.nState==HEAT)?IN:(stateN)((int)kbWork.nState+1);
     return;
   }
  if(kbWork.nState==HEAT || kbWork.nState==COOL)
  {
   kbWork.upb= buttonUp.retentionState();
   kbWork.dnb= buttonDown.retentionState();
   
   if(kbWork.upb!=kbWork.old_upb)
   {
     kbWork.old_upb=kbWork.upb;
     kbWork.roomTarget+=0.5;
   }
   if(kbWork.dnb!=kbWork.old_dnb)
   {
     kbWork.old_dnb=kbWork.dnb;
     kbWork.roomTarget-=0.5;
   }
  }
}
//------------------------------------------------------------------------------------
int findPWMfactor(int setOn,float difTemp)
{
 int val=0;
 float factor=1.0;

 if(setOn==0)
   return 0;


 if(abs(difTemp)<2)
  factor=0.2;
 else if(abs(difTemp)<5)
  factor =0.7;
 
 val= (pow(2, resolution) - 1) * factor;
 return val;
}
//------------------------------------------------------------------------------------
void adjustFANS()
{
  kbWork.fanIN=0;
  kbWork.fanOUT=0;

  if( kbWork.nState==IN)
    kbWork.fanIN=1;
  else if( kbWork.nState==OUT)
    kbWork.fanOUT=1;
 else if( kbWork.nState==HEAT)
  {
    if(((kbWork.roomTarget-kbWork.temperatures[1])>0.5) && (kbWork.difTemp)>0.5)
      kbWork.fanIN=1;
  }
   else if( kbWork.nState==COOL) //COOLas
  {
    if(((kbWork.temperatures[1]-kbWork.roomTarget)>0.5) && (kbWork.difTemp)<-0.5)
      kbWork.fanOUT=1;
  }
 //---------------------------------------------------------------
 bool goFanIn=false;
 bool goFanOut=false;
 if(kbWork.difTemp != kbWork.old_difTemp)
 {
   int rinPWM=findPWMfactor(kbWork.fanIN,kbWork.difTemp);
   goFanIn=(kbWork.inPWM!=rinPWM) && kbWork.fanIN;
   int routPWM=findPWMfactor(kbWork.fanOUT,kbWork.difTemp);
   goFanOut=(kbWork.outPWM!=routPWM) && kbWork.fanOUT;
   kbWork.old_difTemp=kbWork.difTemp;
    Serial.println("diffTemp check");
 }

bool changeFan=false;
 if(kbWork.fanIN!=kbWork.old_fanIN || goFanIn)
  {
    kbWork.inPWM=findPWMfactor(kbWork.fanIN,kbWork.difTemp);
    Serial.println(String("Fan In .......wCycle:")+ String(kbWork.inPWM));
    
    ledcWrite(ledPinFIn ,kbWork.inPWM); 
    kbWork.old_fanIN=kbWork.fanIN;
    changeFan=true;
   }
 if(kbWork.fanOUT!=kbWork.old_fanOUT || goFanOut)
  {
    kbWork.outPWM=findPWMfactor(kbWork.fanOUT,kbWork.difTemp);
    Serial.println(String("Fan Out .......wCycle:") +String(kbWork.outPWM));
  
    ledcWrite(ledPinFOut ,kbWork.outPWM); 
    kbWork.old_fanOUT=kbWork.fanOUT;
    changeFan=true;
  }
 
  if(changeFan)
  {
    int fanMax=max(kbWork.outPWM,kbWork.inPWM);
    int period=400;
    if(fanMax<100)
      period=1000;
    else if(fanMax<150)
      period=800;
    else if(fanMax<200)
      period=600;
    timerHeart.Stop();
    timerHeart.Every(period, drawHeartT);
  }
}
//------------------------------------------------------------------------------------
void loop() {
 memcpy(&old_kbWork,&kbWork,sizeof(kbWork));

 readButtons(); 
 timerHeart.Update();
 timerTemp.Update();
 adjustFANS();

 if( memcmp(&old_kbWork,&kbWork,sizeof(kbWork)) || afterSplash)
 {
    afterSplash=false;
    drawFrame();
    display.display();
 }
}
Description of drawing