Viktor Kurusa
Published

Heating system update with PV system

What do you do if you have to expand the house because of the family expansion :) This means new roof and there are a lot of new free space

ExpertWork in progressOver 12 days262
Heating system update with PV system

Things used in this project

Story

Read more

Code

UnoR4WifiCode

C/C++
#include "RTC.h"
#include <NTPClient.h>
#include "Arduino_LED_Matrix.h"   // Include the LED_Matrix library
#include "WiFiS3.h"
#include <WiFiUdp.h>
#include <WDT.h>
#include "FspTimer.h"
#include "CRC.h"

#include <SPI.h>
#include <mcp2515.h>

//#define DisplaySerial SerialS
#include "Diablo_Serial_4DLib.h"
#include "Diablo_Const4D.h"
#include <SoftwareSerial.h>
//#define DisplaySerial Serial
SoftwareSerial DisplaySerial(2,3) ; // pin 2 = TX of display, pin3 = RX

#define DE 7
#define RE 6

#define RESETLINE 4

//array size for in to char array conversion
#define ARRAY_SIZE 5

#define BUFFER_SIZE 64   // maximális üzenethossz
char DataToBeProcessed[BUFFER_SIZE];
uint8_t dataIndex = 0;

Diablo_Serial_4DLib Display(&DisplaySerial);

struct can_frame RecvcanMsg;
struct can_frame SentcanMsg;

MCP2515 mcp2515(10);

char ssid[] = "KurusaNet";        // your network SSID (name)
char pass[] = "62272311";    // your network password (use for WPA, or use as key for WEP)


IPAddress ip;
int wifiStatus = WL_IDLE_STATUS;
WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
NTPClient timeClient(Udp);

RTCTime currentTime, Default_TimeStamp;         // current time

int keyIndex = 0;                 // your network key index number (needed only for WEP)
int status = WL_IDLE_STATUS;
WiFiServer server(80);

/***********watchdog variable *************/
const long wdtInterval = 2400;

/*********** global variables *************/
unsigned long exectuion_number = 0;
/* CAN */
int first_execution = 0, previousSec = 0, act_sec = 0, CANMsgTimeout = 38, RS485MsgTimeout = 38, previousMin = 0, act_Min = 0, SerialResetErrorCounter = 1, CANResetErrorCounter = 1;
/* Display */
int ScreenNumber = 0, ActScreenNumber = 0, ScreenDrawn = 0, ScreenSelectorButtonState = 0, ScreenSelectorButtonRisingEdge = 0, ScreenSelectorButtonPin = 5;

int AutoManState = 0, AutManStateN = 9999; // 0 - Manual, 1 -Automatic

bool HeatingSeason = false;
unsigned int HeatingState = 0;
#define HeatingBefore2pm 10
#define HeatingAfter2pm 20
#define HeatingAfter10pm 30
int PVHeatingOutput = 0;
int CurrentValue = 0.0;

byte CRCcalc[8];

/* Arrays for the graphical view*/
float T1SecondAvg[60], T1Avg[60], T2SecondAvg[60], T2Avg[60];
int TSecondPtr = 0, TAvgPtr = 0;


struct Temp {
  float Temperature = 40000.0;
  float CAN_Temperature = 40000.0;
  float RS485_Temperature = 40000.0;
  unsigned int QoS = 65534;           /* 1 - OK, 2 - CAN Error, 3 - RS485 Error, Any other - both wrong*/
  unsigned int CAN_QoS = 65534;       /* 1 - OK, Any other - CAN Error*/
  unsigned int RS485_QoS = 65534;       /* 1 - OK, Any other - RS485 Error*/
  unsigned int CAN_timeout = 0;
  unsigned int RS485_timeout = 0;
  byte CAN_PrevMsgCounter = 255;
  byte RS485_PrevMsgCounter = 255;
  RTCTime CAN_TimeStamp = Default_TimeStamp;
  RTCTime RS485_TimeStamp = Default_TimeStamp;
  unsigned int CAN_ErrorState = 0;                          /* 0 - default, 1 - active error occurence, 2 - previous error occurence*/
  RTCTime CAN_ErrorTimeStamp = Default_TimeStamp;
  unsigned int RS485_ErrorState = 0;                        /* 0 - default, 1 - active error occurence, 2 - previous error occurence*/
  RTCTime RS485_ErrorTimeStamp = Default_TimeStamp;
};
/* The N is storing the previous state. If it is different then display it*/
struct Temp T1;
struct Temp T1N;
struct Temp T2;
struct Temp T2N;
struct Temp T3;
struct Temp T3N;
struct Temp T4;
struct Temp T4N;
struct Temp T_Min;
struct Temp T_Max;


struct Flw {
  float Flow = 40000.0;
  float CAN_Flow = 40000.0;
  float RS485_Flow = 40000.0;
  unsigned int QoS = 65534; /* 1 - OK, 2 - CAN Error, 3 - RS485 Error, Any other - both wrong*/
  unsigned int CAN_QoS = 65534;       /* 1 - OK, Any other - CAN Error*/
  unsigned int RS485_QoS = 65534;       /* 1 - OK, Any other - RS485 Error*/  
  unsigned int CAN_timeout = 0;
  unsigned int RS485_timeout = 0;
  byte CAN_PrevMsgCounter = 0;
  byte RS485_PrevMsgCounter = 0;
  RTCTime CAN_TimeStamp = Default_TimeStamp;
  RTCTime RS485_TimeStamp = Default_TimeStamp;
  unsigned int CAN_ErrorState = 0;                          /* 0 - default, 1 - active error occurence, 2 - previous error occurence*/
  RTCTime CAN_ErrorTimeStamp = Default_TimeStamp;
  unsigned int RS485_ErrorState = 0;                        /* 0 - default, 1 - active error occurence, 2 - previous error occurence*/
  RTCTime RS485_ErrorTimeStamp = Default_TimeStamp;
};
/* The N is storing the previous state. If it is different then display it*/
struct Flw F3;
struct Flw F3N;
struct Flw F4;
struct Flw F4N;



struct Valve {
  int State = 2; /* 0 - closed, 1 - opened, 2 - error*/
  unsigned int QoS = 65534;
  unsigned int timeout = 0;
};

/* The N is storing the previous state. If it is different then display it*/
struct Valve V1;
struct Valve V1N;
struct Valve V2;
struct Valve V2N;



struct Pump {
  int State = 2; /* 0 - stopped, 1 - running, 2 - error*/
  unsigned int QoS = 65534;
  unsigned int timeout = 0;
};
/* The N is storing the previous state. If it is different then display it*/
struct Pump P1;
struct Pump P1N;


struct Relay {
  int State = 2; /* 0 - off, 1 - on, 2 - error*/
  unsigned int QoS = 65534; /* 1 - OK, anything other - wrong*/
  unsigned int timeout = 0;
};

struct Relay PVHeating;
struct Relay PVHeatingN;

void setup(){

  pinMode(ScreenSelectorButtonPin, INPUT_PULLUP);
  //pinMode(13, OUTPUT);

  // Set the DE and RE pins as outputs
  pinMode(DE, OUTPUT);
  pinMode(RE, OUTPUT);
  
  // Set DE and RE LOW to disable transmission mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  Serial.begin(115200);
  Serial1.begin(115200);
  Serial.println("Wifi Conn");
  connectToWiFi();
  Serial.println("RTC startup");
  RTC.begin();
  Serial.println("Timer begin");
  timeClient.begin();
  timeClient.update();
  server.begin(); 

  SentcanMsg.can_id  = 0x060;
  SentcanMsg.can_dlc = 8;
  SentcanMsg.data[0] = 0x00;
  SentcanMsg.data[1] = 0x00;
  SentcanMsg.data[2] = 0x00;
  SentcanMsg.data[3] = 0x00;
  SentcanMsg.data[4] = 0x00;
  SentcanMsg.data[5] = 0x00;
  SentcanMsg.data[6] = 0x00;
  SentcanMsg.data[7] = 0x00;

  RecvcanMsg.can_id  = 0x110;
  RecvcanMsg.can_dlc = 8;
  RecvcanMsg.data[0] = 0x00;
  RecvcanMsg.data[1] = 0x00;
  RecvcanMsg.data[2] = 0x00;
  RecvcanMsg.data[3] = 0x00;
  RecvcanMsg.data[4] = 0x00;
  RecvcanMsg.data[5] = 0x00;
  RecvcanMsg.data[6] = 0x00;
  RecvcanMsg.data[7] = 0x00;

  Serial.println("CAN Begin");
  mcp2515.reset();
  mcp2515.setBitrate(CAN_500KBPS);
  mcp2515.setNormalMode();

  Serial.println("WDT setup");
  WDT.begin(wdtInterval);
  WDT.refresh();
  

  Timer_update();
  CANMsgSending();

  Serial.println("Startup");
}

void loop(){
  /*Update the RTC @ in every hour to compensate the shifting*/
  if(currentTime.getMinutes() >= 59){
Serial.println("timer update");
    Timer_update();
    
    //CANMsgSending();

  }
  /*Reading the actual time*/
  RTC.getTime(currentTime);
  /*Getting the actual sec*/
  act_sec = currentTime.getSeconds();
  act_Min = currentTime.getMinutes();

  /*checking the screen selector input pin*/
  digitalInputReading(ScreenSelectorButtonPin, &ScreenSelectorButtonState, &ScreenSelectorButtonRisingEdge, first_execution);
  CANMsgReceiving();
  // Set DE and RE LOW to disable transmission mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW); 
  RS485MsgReceiving();
  CAN_RS485_MsgTimeOutCheck();
  SignalAvailabilityCheckAndSelection();
  DisplayHandling();
  ChangeUpdate();
  TempAvgCounting();
  Webserver();

  WDT.refresh();

  /*increment the execution number*/  
  exectuion_number++;
  /*compare it with a limit and set the variable*/
  if(exectuion_number > 3){
    first_execution = 1;
  } else {
    first_execution = 0;
  }

}



void DisplayHandling(){
  if(ScreenSelectorButtonRisingEdge){
    ScreenNumber++;
    /*1since only 3 screens are implemented, the number shall be limited to 3*/
  }
  if(ScreenNumber > 2){
    ScreenNumber = 0;
  }

  /*The static object shall be drawn if the act screen number is different from the actual*/
  if(ScreenNumber != ActScreenNumber){
        ActScreenNumber = ScreenNumber;
        ScreenDrawn = 0;
  }


  switch(ScreenNumber){
      case 0:
          /*drawing the main screen*/
          if(!ScreenDrawn){
              DisplaySerial.begin(9600);
              Display.TimeLimit4D = 5000; // 5 second timeout on all commands
              Display.gfx_Cls();
              Display.gfx_BGcolour(BLACK);           // reset
              Display.txt_BGcolour(WHITE);           // reset
              Display.gfx_ScreenMode(LANDSCAPE);
              /*Displaying the buffer*/
              Display.gfx_Rectangle(50,50, 150,200, BLUE);              
              Display.gfx_Line(40, 85, 60, 85, LIGHTGREY);
              Display.gfx_Line(40, 155, 60, 155, LIGHTGREY);
              /*Displaying the kazán :-)*/
              Display.gfx_Rectangle(250, 200, 350, 250, LIGHTGREY);
              /*Dispalying the radiators*/
              Display.gfx_Rectangle(400, 50, 460, 150, LIGHTGREY);
              
              /*Displaying the pipes*/
              /*heating Horizontal red pinpe*/
              Display.gfx_Line(150, 70, 400, 70, RED);
              /*Heating Veritcal red pipe*/
              Display.gfx_Line(285, 70, 285, 200, RED);

              /*heating Horizontal blue pinpe*/
              Display.gfx_Line(150, 140, 400, 140, BLUE);
              /*Heating Veritcal blue pipe*/
              Display.gfx_Line(315, 140, 315, 200, BLUE);

              /*Heating Veritcal blue pipe from the valve to the pipe*/
              Display.gfx_Line(240, 95, 240, 140, BLUE);

              /*Hot water pipe from Buffer*/
              Display.gfx_Line(100, 200, 100, 270, BLUE);
              /* Hot water pipe from Boiler to valve*/
              Display.gfx_Line(100, 225, 250, 225, BLUE);

            ScreenDrawn = 1; 
          }

          Display.txt_BGcolour(BLACK);
          Display.txt_FGcolour(WHITE);

          if(AutoManState != AutManStateN){
            Display.txt_FontID(FONT_4);
            Display.txt_MoveCursor(0,1);

            if(AutoManState){
              Display.putstr("AUT mode");

            } else {
              Display.putstr("MAN mode");

            }
            
          }

          if(PVHeatingOutput){
            Display.gfx_Rectangle(60,80, 90,160, GREEN);

          } else {
            Display.gfx_Rectangle(60,80, 90,160, WHITE);

          }

          Display.txt_FontID(FONT_4);
          Display.txt_MoveCursor(7,3);
          Display.putstr("I=");
          /* Converting the received float to char array*/
          char buffer1[10];
          dtostrf(CurrentValue, 4, 1, buffer1);
          Display.txt_MoveCursor(7,4);
          Display.putstr(buffer1);


          Display.txt_BGcolour(YELLOW);
          Display.txt_FGcolour(BLACK);
          Display.txt_FontID(FONT_4);
          Display.txt_MoveCursor(18,10);
          if(HeatingSeason){
            switch(HeatingState){
              case HeatingBefore2pm: {
                  Display.putstr("Futes szezon: futes 2 elott");
                break;
              }
              case HeatingAfter2pm:{
                  Display.putstr("Futes szezon: futes 2 utan");
                break;
              }
              case HeatingAfter10pm:{
                  Display.putstr("Futes szezon: futes 10 utan");
                break;
              }

              default:{
                  Display.putstr("Futes szezon: default");
              }
            }
          } else {
              Display.putstr("Nincs Futes szezon");
          }
          

          char IPAddress[3];
          IPAddress[0] = ((ip[0] / 100) + 48);
          IPAddress[1] = (((ip[0] % 100) / 10) + 48);
          IPAddress[2] = (((ip[0] % 100) % 10) + 48);
          Display.txt_BGcolour(YELLOW);
          Display.txt_FGcolour(BLACK);
          Display.txt_FontID(FONT_4);
          Display.txt_MoveCursor(19,4);
          Display.putstr(IPAddress);
          Display.txt_MoveCursor(19,7);
          Display.putstr(".");
          Display.txt_MoveCursor(19,8);
          IPAddress[0] = ((ip[1] / 100) + 48);
          IPAddress[1] = (((ip[1] % 100) / 10) + 48);
          IPAddress[2] = (((ip[1] % 100) % 10) + 48);
          Display.putstr(IPAddress);
          Display.txt_MoveCursor(19,11);
          Display.putstr(".");
          Display.txt_MoveCursor(19,12);
          IPAddress[0] = ((ip[2] / 100) + 48);
          IPAddress[1] = (((ip[2] % 100) / 10) + 48);
          IPAddress[2] = (((ip[2] % 100) % 10) + 48);
          Display.putstr(IPAddress);
          Display.txt_MoveCursor(19,15);
          Display.putstr(".");
          Display.txt_MoveCursor(19,16);
          IPAddress[0] = ((ip[3] / 100) + 48);
          IPAddress[1] = (((ip[3] % 100) / 10) + 48);
          IPAddress[2] = (((ip[3] % 100) % 10) + 48);
          Display.putstr(IPAddress);

          


           if(previousSec != act_sec){
            int DataToBeSent[6];
            char DataInChar[30] = "";
            String ToBeDisplayed = "";

            DataToBeSent[0] = currentTime.getYear();
            DataToBeSent[1] = Month2int(currentTime.getMonth());
            DataToBeSent[2] = currentTime.getDayOfMonth();
            DataToBeSent[3] = currentTime.getHour();
            DataToBeSent[4] = currentTime.getMinutes();
            DataToBeSent[5] = act_sec;

              /*adding all data to the string to be displayed*/
            for(int i = 0; i < 6; i++){
                  ToBeDisplayed+=String(DataToBeSent[i]);
                  ToBeDisplayed+=":";
            }
            /*The char array can be displayed*/
            ToBeDisplayed.toCharArray(DataInChar, 30);
            Display.txt_MoveCursor(0,20);
            Display.putstr(DataInChar);
          }


          /* The dynaimc elements shall be checked from element to element for a change*/
          if((P1.State != P1N.State)||(P1.QoS != P1N.QoS)){

            /*Update the P1N to P1 values*/
            /*If the QoS is wrong the background shall be red,the front color shall be white*/
            if((P1.QoS != 1)||(P1.State == 2)){
              Display.gfx_CircleFilled(190, 70, 20, RED);
              Display.gfx_Triangle(185,85,185,55,207,70,YELLOW);

            } else if(P1.State == 0){
              /* If the pump is stopped*/
              Display.gfx_CircleFilled(190, 70, 20, YELLOW);
              Display.gfx_Triangle(185,85,185,55,207,70,BLACK);              


            } else if(P1.State == 1){
              /* If the pump is running*/
              Display.gfx_CircleFilled(190, 70, 20, GREEN);
              Display.gfx_Triangle(185,85,185,55,207,70,WHITE);  

            }
          }


          if((V1.State != V1N.State)||(V1.QoS != V1N.QoS)){

            /*Update the V1N to V1 values*/
            /*If the QoS is wrong the background shall be red,the front color shall be white*/
            if((V1.QoS != 1)||(V1.State > 2)){
              /*Left*/
              Display.gfx_TriangleFilled(220,85,220,55,240,70,RED);
              /*Right*/
              Display.gfx_TriangleFilled(260,85,260,55,240,70,RED);
              /*Lower*/
              Display.gfx_TriangleFilled(230,95,250,95,240,70,RED);

            } else if(V1.State == 2){
              /* If the valve is set to Boiler */
              /*Left*/
              Display.gfx_TriangleFilled(220,85,220,55,240,70,GREEN);
              /*Right*/
              Display.gfx_TriangleFilled(260,85,260,55,240,70,RED);
              /*Lower*/
              Display.gfx_TriangleFilled(230,95,250,95,240,70,GREEN);

            } else if(V1.State == 1){
              /* If the valve is set to Buffer */
              /*Left*/
              Display.gfx_TriangleFilled(220,85,220,55,240,70,GREEN);
              /*Right*/
              Display.gfx_TriangleFilled(260,85,260,55,240,70,GREEN);
              /*Lower*/
              Display.gfx_TriangleFilled(230,95,250,95,240,70,RED);
            }
          }


          if((V2.State != V2N.State)||(V2.QoS != V2N.QoS)){

            /*Update the V2N to V2 values*/
            /*If the QoS is wrong the background shall be red,the front color shall be white*/
            if((V2.QoS != 1)||(V2.State > 2)){
                /*Upper*/
                Display.gfx_TriangleFilled(85,205,115,205,100,225,RED);
                /*Lower*/
                Display.gfx_TriangleFilled(85,245,115,245,100,225,RED);
                /*Right*/
                Display.gfx_TriangleFilled(125,210,125,235,100,225,RED);

            } else if(V2.State == 2){
                /*Upper*/
                Display.gfx_TriangleFilled(85,205,115,205,100,225,RED);
                /*Lower*/
                Display.gfx_TriangleFilled(85,245,115,245,100,225,GREEN);
                /*Right*/
                Display.gfx_TriangleFilled(125,210,125,235,100,225,GREEN);

            } else if(V2.State == 1){
                /*Upper*/
                Display.gfx_TriangleFilled(85,205,115,205,100,225,GREEN);
                /*Lower*/
                Display.gfx_TriangleFilled(85,245,115,245,100,225,RED);
                /*Right*/
                Display.gfx_TriangleFilled(125,210,125,235,100,225,GREEN);

            }
          }

Serial.print("T1.QoS: ");               
Serial.println(T1.QoS); 
          /* 1 - OK, 2 - CAN Error, 3 - RS485 Error */
          if((T1.Temperature != T1N.Temperature)||(T1.QoS != T1N.QoS)){

            if((T1.QoS != 1) && (T1.QoS != 2) && (T1.QoS != 3)){
             
                Display.txt_BGcolour(RED);
                Display.txt_FGcolour(WHITE);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(11,5);
                Display.putstr("T1=ERR");

            } else if((T1.Temperature != T1N.Temperature)){
              if((T1.QoS == 1) && (T1.QoS != 2) && (T1.QoS != 3)){
                Display.txt_BGcolour(BLACK);
                Display.txt_FGcolour(WHITE);

              } else if((T1.QoS != 1) && (T1.QoS == 2) && (T1.QoS != 3)){
                Display.txt_BGcolour(YELLOW);
                Display.txt_FGcolour(BLACK);

              } else if((T1.QoS != 1) && (T1.QoS != 2) && (T1.QoS == 3)){
                Display.txt_BGcolour(ORANGE);
                Display.txt_FGcolour(WHITE);

              }
                
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(11,5);
                Display.putstr("T1=");
                /* Converting the received float to char array*/
                char buffer[10];
                dtostrf(T1.Temperature, 4, 1, buffer);
                Display.txt_MoveCursor(11,8);
                Display.putstr(buffer);

            }
          }


          if((T2.Temperature != T2N.Temperature)||(T2.QoS != T2N.QoS)){          
                
            if((T2.QoS != 1) && (T2.QoS != 2) && (T2.QoS != 3)){
                Display.txt_BGcolour(RED);
                Display.txt_FGcolour(WHITE);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(4,5);
                Display.putstr("T2=ERR");

            } else if((T2.Temperature != T2N.Temperature)){
              if((T2.QoS == 1) && (T2.QoS != 2) && (T2.QoS != 3)){
                Display.txt_BGcolour(BLACK);
                Display.txt_FGcolour(WHITE);

              } else if((T2.QoS != 1) && (T2.QoS == 2) && (T2.QoS != 3)){
                Display.txt_BGcolour(YELLOW);
                Display.txt_FGcolour(BLACK);

              } else if((T2.QoS != 1) && (T2.QoS != 2) && (T2.QoS == 3)){
                Display.txt_BGcolour(ORANGE);
                Display.txt_FGcolour(WHITE);

              }
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(4,5);
                Display.putstr("T2=");
                /* Converting the received float to char array*/
                char buffer[10];
                dtostrf(T2.Temperature, 4, 1, buffer);
                Display.txt_MoveCursor(4,8);
                Display.putstr(buffer);

            }
          }

/*          if((T3.Temperature != T3N.Temperature)||(T3.QoS != T3N.QoS)){

            if(T3.QoS != 1){
                Display.txt_BGcolour(RED);
                Display.txt_FGcolour(WHITE);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(3,22);
                Display.putstr("T3=ERR");

            } else if((T3.Temperature != T3N.Temperature)){
                Display.txt_BGcolour(BLACK);
                Display.txt_FGcolour(WHITE);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(3,22);
                Display.putstr("T3=");
                /* Converting the received float to char array*/
/*                char buffer[10];
                dtostrf(T3.Temperature, 4, 1, buffer);
                Display.txt_MoveCursor(3,25);
                Display.putstr(buffer);

            }
          }*/

/*          if((T4.Temperature != T4N.Temperature)||(T4.QoS != T4N.QoS)){

            if(T4.QoS != 1){
                Display.txt_BGcolour(RED);
                Display.txt_FGcolour(WHITE);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(17,5);
                Display.putstr("T4=ERR");

            } else if((T4.Temperature != T4N.Temperature)){
                Display.txt_BGcolour(BLACK);
                Display.txt_FGcolour(WHITE);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(17,5);
                Display.putstr("T4=");
                /* Converting the received float to char array*/
/*                char buffer[10];
                dtostrf(T4.Temperature, 4, 1, buffer);
                Display.txt_MoveCursor(17,8);
                Display.putstr(buffer);

            }
          }*/
/*Serial.print("F3.QoS: ");               
Serial.println(F3.QoS);*/
          if((F3.Flow != F3N.Flow)||(F3.QoS != F3N.QoS)){

            if((F3.QoS != 1) && (F3.QoS != 2) && (F3.QoS != 3)){
                Display.txt_BGcolour(RED);
                Display.txt_FGcolour(WHITE);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(2,22);
                Display.putstr("F3=ERR");

            } else if((F3.Flow != F3N.Flow)){
                if((F3.QoS == 1) && (F3.QoS != 2) && (F3.QoS != 3)){
                  Display.txt_BGcolour(BLACK);
                  Display.txt_FGcolour(WHITE);

                } else if((F3.QoS != 1) && (F3.QoS == 2) && (F3.QoS != 3)){
                  Display.txt_BGcolour(YELLOW);
                  Display.txt_FGcolour(BLACK);

                } else if((F3.QoS != 1) && (F3.QoS != 2) && (F3.QoS == 3)){
                  Display.txt_BGcolour(ORANGE);
                  Display.txt_FGcolour(WHITE);

                }

                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(2,22);
                Display.putstr("F3=");
                /* Converting the received float to char array*/
                char buffer[10];
                dtostrf(F3.Flow, 4, 1, buffer);
                Display.txt_MoveCursor(2,25);
                Display.putstr(buffer);

            }
          }

/*Serial.print("F4.QoS: ");               
Serial.println(F4.QoS); */
          if((F4.Flow != F4N.Flow)||(F4.QoS != F4N.QoS)){

            if((F4.QoS != 1) && (F4.QoS != 2) && (F4.QoS != 3)){
                Display.txt_BGcolour(RED);
                Display.txt_FGcolour(WHITE);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(16,5);
                Display.putstr("f4=ERR");

            } else if((F4.Flow != F4N.Flow)){
                if((F4.QoS == 1) && (F4.QoS != 2) && (F4.QoS != 3)){
                  Display.txt_BGcolour(BLACK);
                  Display.txt_FGcolour(WHITE);

                } else if((F4.QoS != 1) && (F4.QoS == 2) && (F4.QoS != 3)){
                  Display.txt_BGcolour(YELLOW);
                  Display.txt_FGcolour(BLACK);

                } else if((F4.QoS != 1) && (F4.QoS != 2) && (F4.QoS == 3)){
                  Display.txt_BGcolour(ORANGE);
                  Display.txt_FGcolour(WHITE);

                }
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(16,5);
                Display.putstr("F4=");
                /* Converting the received float to char array*/
                char buffer[10];
                dtostrf(F4.Flow, 4, 1, buffer);
                Display.txt_MoveCursor(16,8);
                Display.putstr(buffer);

            }
          }

        break;

      case 1: /* Drawing the temperature trend graph*/
          
          if((!ScreenDrawn) || (previousMin != act_Min)){

                int TempDataPtr1 = 59, TempDataPtr2 = 58, X1 = 50;

                /*Drawing the x and y axis*/
                Display.gfx_Cls();
                Display.gfx_Line(50, 30, 50, 270, LIGHTGREY);
                Display.gfx_Line(50, 270, 450, 270, LIGHTGREY);

                Display.txt_BGcolour(BLACK);
                Display.txt_FGcolour(RED);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(1,10);
                Display.putstr("T1");

                Display.txt_BGcolour(BLACK);
                Display.txt_FGcolour(YELLOW);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(1,15);
                Display.putstr("T2");

                Display.txt_BGcolour(BLACK);
                Display.txt_FGcolour(WHITE);
                Display.txt_FontID(FONT_4);
                Display.txt_MoveCursor(2,0);
                Display.putstr("100C");

                Display.txt_MoveCursor(5,0);
                Display.putstr("75C");

                Display.txt_MoveCursor(9,0);
                Display.putstr("50C");

                Display.txt_MoveCursor(12,0);
                Display.putstr("25C");

                Display.txt_MoveCursor(16,0);
                Display.putstr(" 0C");

                Display.txt_MoveCursor(18,5);
                Display.putstr("60min");

                Display.txt_MoveCursor(18,20);
                Display.putstr("30min");

                Display.txt_MoveCursor(18,33);
                Display.putstr("Act T");

                /*Drawing the trends from lines*/
                /*First the y value shall be calculated from the temperature value 
                * with the following formula: y = -2.4x + 270*/
                for(int i = 0; i < 59; i++){
                    int T1Y1 = (int)((-2.4 * T1Avg[TempDataPtr1]) + 270);
                    int T1Y2 = (int)((-2.4 * T1Avg[TempDataPtr2]) + 270);
                    int T2Y1 = (int)((-2.4 * T2Avg[TempDataPtr1]) + 270);
                    int T2Y2 = (int)((-2.4 * T2Avg[TempDataPtr2]) + 270);                       
                    int X2 = X1 + 7; 
                    Display.gfx_Line(X1, T1Y1, X2, T1Y2, RED);
                    Display.gfx_Line(X1, T2Y1, X2, T2Y2, YELLOW);
                    TempDataPtr1 = TempDataPtr2;
                    TempDataPtr2--;
                    X1 = X2;
                }

                ScreenDrawn = 1;
          }
        break;

      case 2: /* Drawing the error table */
          
          if(!ScreenDrawn){
              ScreenDrawn = 1;
              /* Drawing the table */
              /*Cloumns*/
              Display.gfx_Cls();
              Display.gfx_Line(5, 5, 5, 290, LIGHTGREY);
              Display.gfx_Line(40, 5, 40, 290, LIGHTGREY);
              Display.gfx_Line(105, 5, 105, 290, LIGHTGREY);
              Display.gfx_Line(270, 5, 270, 315, LIGHTGREY);
              
              /*Top*/
              Display.gfx_Line(5, 5, 470, 5, LIGHTGREY);
              /*T1*/
              /*Line next to T1*/
              Display.gfx_Line(40, 29, 270, 29, LIGHTGREY);
              /*Line under T1*/
              Display.gfx_Line(5, 55, 270, 55, LIGHTGREY);
              /*Line next to T2*/
              Display.gfx_Line(40, 78, 270, 78, LIGHTGREY);
              /*Line under T2*/
              Display.gfx_Line(5, 100, 270, 100, LIGHTGREY);
              /*Line next to T3*/
              Display.gfx_Line(40, 122, 270, 122, LIGHTGREY);
              /*Line under T3*/
              Display.gfx_Line(5, 145, 470, 145, LIGHTGREY);
              /*Line next to T4*/
              Display.gfx_Line(40, 169, 270, 169, LIGHTGREY);
              /*Line under T4*/
              Display.gfx_Line(5, 192, 270, 192, LIGHTGREY);
              /*Line next to F3*/
              Display.gfx_Line(40, 218, 270, 218, LIGHTGREY);
              /*Line under F3*/
              Display.gfx_Line(5, 242, 270, 242, LIGHTGREY);
              /*Line next to F4*/
              Display.gfx_Line(40, 270, 270, 270, LIGHTGREY);
              /*Line under F4*/
              Display.gfx_Line(5, 290, 270, 290, LIGHTGREY);

              Display.txt_BGcolour(BLACK);
              Display.txt_FGcolour(WHITE);
              Display.txt_FontID(FONT_4);
              Display.txt_MoveCursor(2,1);
              Display.putstr("T1");

              Display.txt_BGcolour(BLACK);
              Display.txt_FGcolour(WHITE);
              Display.txt_FontID(FONT_4);
              Display.txt_MoveCursor(5,1);
              Display.putstr("T2");

              Display.txt_BGcolour(BLACK);
              Display.txt_FGcolour(WHITE);
              Display.txt_FontID(FONT_4);
              Display.txt_MoveCursor(8,1);
              Display.putstr("T3");

              Display.txt_BGcolour(BLACK);
              Display.txt_FGcolour(WHITE);
              Display.txt_FontID(FONT_4);
              Display.txt_MoveCursor(11,1);
              Display.putstr("T4");

              Display.txt_BGcolour(BLACK);
              Display.txt_FGcolour(WHITE);
              Display.txt_FontID(FONT_4);
              Display.txt_MoveCursor(13,1);
              Display.putstr("F3");

              Display.txt_BGcolour(BLACK);
              Display.txt_FGcolour(WHITE);
              Display.txt_FontID(FONT_4);
              Display.txt_MoveCursor(16,1);
              Display.putstr("F4");

              Display.txt_BGcolour(BLACK);
              Display.txt_FGcolour(WHITE);
              Display.txt_FontID(FONT_4);
              Display.txt_MoveCursor(2,23);
              Display.putstr("Controller");

              Display.txt_BGcolour(BLACK);
              Display.txt_FGcolour(WHITE);
              Display.txt_FontID(FONT_4);
              Display.txt_MoveCursor(11,23);
              Display.putstr("Checker");              
          }

          if(previousSec != act_sec){

            int CANErrorLine = 1, RS485ErrorLine = 3;
            T_CommErrors(&T1, CANErrorLine, RS485ErrorLine);
            CANErrorLine+=4;
            RS485ErrorLine+=4;
            T_CommErrors(&T2, CANErrorLine, RS485ErrorLine);
            CANErrorLine+=4;
            RS485ErrorLine+=4;
           // T_CommErrors(&T3, CANErrorLine, RS485ErrorLine);
            CANErrorLine+=4;
            RS485ErrorLine+=4;
           // T_CommErrors(&T4, CANErrorLine, RS485ErrorLine);
            CANErrorLine+=4;
            RS485ErrorLine+=4;
            F_CommErrors(&F3, CANErrorLine, RS485ErrorLine);
            CANErrorLine+=4;
            RS485ErrorLine+=4;
            F_CommErrors(&F4, CANErrorLine, RS485ErrorLine);

            /*Controller CAN*/
            Display.txt_BGcolour(ORANGE);
            Display.txt_FGcolour(BLACK);
            Display.txt_FontID(FONT_3);
            Display.txt_MoveCursor(5,35);
            Display.putstr("  CAN  ");
            /*CAN Error timestamp*/
            Display.txt_BGcolour(ORANGE);
            Display.txt_FGcolour(BLACK);
            Display.txt_FontID(FONT_3);
            Display.txt_MoveCursor(7,35);
            Display.putstr("2025:08:08 14:09:10");  
            /*Controller RS485*/
            Display.txt_BGcolour(RED);
            Display.txt_FGcolour(WHITE);
            Display.txt_FontID(FONT_3);
            Display.txt_MoveCursor(9,35);
            Display.putstr(" RS485 ");
            /*RS485 Error timestamp*/
            Display.txt_BGcolour(RED);
            Display.txt_FGcolour(WHITE);
            Display.txt_FontID(FONT_3);
            Display.txt_MoveCursor(11,35);
            Display.putstr("2025:08:08 14:09:10");


            /*Checker CAN*/
            Display.txt_BGcolour(ORANGE);
            Display.txt_FGcolour(BLACK);
            Display.txt_FontID(FONT_3);
            Display.txt_MoveCursor(17,35);
            Display.putstr("  CAN  ");
            /*CAN Error timestamp*/
            Display.txt_BGcolour(ORANGE);
            Display.txt_FGcolour(BLACK);
            Display.txt_FontID(FONT_3);
            Display.txt_MoveCursor(19,35);
            Display.putstr("2025:08:08 14:09:10");  
            /*Checker RS485*/
            Display.txt_BGcolour(RED);
            Display.txt_FGcolour(WHITE);
            Display.txt_FontID(FONT_3);
            Display.txt_MoveCursor(21,35);
            Display.putstr(" RS485 ");
            /*RS485 Error timestamp*/
            Display.txt_BGcolour(RED);
            Display.txt_FGcolour(WHITE);
            Display.txt_FontID(FONT_3);
            Display.txt_MoveCursor(23,35);
            Display.putstr("2025:08:08 14:09:10");

          }
       break;
  }

}

void T_CommErrors(struct Temp *T, int CAN_Line, int RS485_Line){

            if(T->CAN_ErrorState == 1){

                  Display.txt_BGcolour(RED);
                  Display.txt_FGcolour(WHITE);
                  Display.txt_FontID(FONT_3);
                  Display.txt_MoveCursor(CAN_Line,6);
                  Display.putstr("  CAN  ");
                  /*CAN Error timestamp*/
                  Display.txt_BGcolour(RED);
                  Display.txt_FGcolour(WHITE);
                  Display.txt_FontID(FONT_3);
                  PutErrorTimeStampToScreen(CAN_Line, 14, T->CAN_ErrorTimeStamp);

            }  else {

                  Display.txt_BGcolour(ORANGE);
                  Display.txt_FGcolour(BLACK);
                  Display.txt_FontID(FONT_3);
                  Display.txt_MoveCursor(1,6);
                  Display.putstr("  CAN  ");
...

This file has been truncated, please download it to see its full contents.

ControllerCode

C/C++
#include <avr/wdt.h>
#include <SPI.h>
#include <mcp2515.h>
#include "CRC.h"
#include <Arduino.h>
#include <RtcDS1302.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20 column and 4 rows


struct can_frame RecvcanMsg;
struct can_frame SentcanMsg;
MCP2515 mcp2515(53);

#define DisplaySerial Serial

#define DE 23
#define RE 22

#define BUFFER_SIZE 64   // maximális üzenethossz
char DataToBeProcessed[BUFFER_SIZE];
uint8_t dataIndex = 0, dataIndexCurrent = 0;

#define BoilerSelectedInputPinNumber 33
#define BufferSelectedInputPinNumber 35



// DS1302 RTC instance
// CONNECTIONS:
// DS1302 CLK/SCLK --> 5
// DS1302 DAT/IO --> 4
// DS1302 RST/CE --> 2
// DS1302 VCC --> 3.3v - 5v
// DS1302 GND --> GND
ThreeWire myWire(3,5,2); // IO, SCLK, CE
RtcDS1302<ThreeWire> Rtc(myWire);

/*********** global variables *************/
bool HeatingSeason = false;
unsigned int HeatingState = 0;
#define HeatingBefore2pm 10
#define HeatingAfter2pm 20
#define HeatingAfter10pm 30
unsigned long exectuion_number = 0;
/* CAN */
int first_execution = 0, previousSec = 0, actSecond = 0, CANMsgTimeout = 38, RS485MsgTimeout = 38, previousMin = 0, act_Min = 0, act_Hour = 0, act_Month = 0, SerialResetErrorCounter = 1, CANResetErrorCounter = 1;
/* Display */
int ScreenNumber = 0, ActScreenNumber = 0, ScreenDrawn = 0, ScreenSelectorButtonState = 0, ScreenSelectorButtonRisingEdge = 0, ScreenSelectorButtonPin = 36;

/* Arrays for the graphical view*/
float T1SecondAvg[60], T1Avg[60], T2SecondAvg[60], T2Avg[60];
int TSecondPtr = 0, TAvgPtr = 0;

int HeatSource = 0, HeatSource_N = 0;  /* 0 - Boiler, 1 - Buffer tank*/
int HeatingRequest = 0; /* It is the signal of the thermostat. 0 - No request, 1 - heating is needed*/
float BufferTankTempLowLimit = 40.0, BufferTankTempHighLimit = 80.0, TempDifferentInsisdeBuffertank = 7.0, TemperatureDifference = 20.0, PVHeatingTemperatureMin = 50.0, ElectricalHeatingMinTemp = 40.0;
unsigned int PVHeatingMinutes = 0, PVHeatingMinutesLimit = 2;
int ValveSelection = 0; /* feedbacks the valve state: 2 - Boiler is selected, 1 - Buffer tank is selected, 3 - Circulation, any other - error*/
int ValveError = 0, ValveErrorCounter = 0, ValveTimoutErrorLimit = 55; /* Counter for counting the valve errors*/
unsigned int ValveTimeOut = 0, ValveTimeOutLimit = 15, ValveDelay = 7, ValveDelayElapsed = 0; /*These are used for the pump start condition checking*/

#define PumpNotRunning 10
#define PumpRunning 20
#define PumpStarting 30
#define PumpErrorState 40

float FlowLowLimit = 0.0;
int PumpControlState = 10, PumpCommand = 0, PumpStartRequest = 0; /* 0 - not running, 1 - running*/
int PumpError = 0, PumpErrorCounter = 0, PumpErrorLimit = 57, PumpStartupCounter = 4; /* Counter for counting the pump errors*/

/**************** Current measurement *****************/
byte CurrentStringInChar[40] = "";
uint8_t CurrentdataIndex = 0;
byte CurrentMsgCount = 0;
int CurrentMsgError = 1;
float CurrentChannel0 = 0.0, CurrentChannel1 = 0.0, CurrentChannel2 = 0.0;


/**************** Digital inputs and outputs *******************/
int HeatSourceBoilerInputPin = BoilerSelectedInputPinNumber, HeatSourceBoilerInput = 0, HeatSourceBoilerInputRisingEdge = 0, HeatSourceBufferInputPin = BufferSelectedInputPinNumber, HeatSourceBufferInput = 0, HeatSourceBufferInputRisingEdge = 0; 
int HeatSourceSelectorOutputPin = 10;
int V1BufferInputPin = BufferSelectedInputPinNumber, V1BufferInput = 0, V1BufferInputRisingEdge = 0, V1BoilerInputPin = BoilerSelectedInputPinNumber, V1BoilerInput = 0, V1BoilerInputRisingEdge = 0;
int V2BufferInputPin = BufferSelectedInputPinNumber, V2BufferInput = 0, V2BufferInputRisingEdge = 0, V2BoilerInputPin = BoilerSelectedInputPinNumber, V2BoilerInput = 0, V2BoilerInputRisingEdge = 0;
int PumpStartOutputPin = 11, PumpRelayInputPin = 38, PumpRelayInput = 0, PumpRelayInputRisingEdge = 0;
int ThermostatInputPin = 32, ThermostatInput = 0, ThermostatInputRisingEdge = 0;
int BoilerCircuitOutputPin = 0, BoilerCircuitOutput = 0;
int PumpManualModeStartInputPin = 43, PumpManualModeStartInput = 0, PumpManualStartInputRisingEdge = 0;
int PVManualModeHeatingInputPin = 42, PVManualModeHeatingInput = 0, PVManualHeatingInputRisingEdge = 0;
int PVHeatingOutput = 0, PVHeatingOutputN1 = 0, PVHeatingOutputRisingEdge = 0, PVHeatingOutputFallingEdge = 0;
int PVHeatingSSROutputT_ONDelay = 2, PVHeatingSSROutputT_ONDelayCounter = 0, PVHeatingRelayOutputT_OFFDelay = 2, PVHeatingRelayOutputT_OFFDelayCounter = 0; 
int PVHeatingSSROutputPin = 7, PVHeatingSSROutput = 0, PVHeatingOutputPin = 8, PVHeatingRelayOutput = 0;
int PVHeatingRelayInputPin = 41, PVHeatingRelayInput = 0, PVHeatingRelayInputRisingEdge = 0;
int AutoModeInputPin = 49, AutoModeInput = 0, ManModeInputPin = 48, ManModeInput = 0;
int ManModeBoilerSelectionInputPin = 46, ManModeBoilerSelectionInput = 0, ManModeBufferSelectionInputPin = 47, ManModeBufferSelectionInput = 0;
int AutoManState = 0, AutoManStateN = 9999; // 0 - Manual, 1 -Automatic
int AutoModeLEDOutputPin = 9, AutoModeLEDOutput = 0, ControllerWorkingOutputPin = 13, ControllerWorking = 0;
byte MsgCount = 0;

byte CRCcalc[8];


char defaultDate[10] = {"1987:03:11"};

char defaultTime[8] = {"04:30:00"};

RtcDateTime Default_TimeStamp = RtcDateTime(defaultDate, defaultTime);

/* The possible temperature change between messages and the plausibility limits*/
float TemperatureChangeLimit = 10.0, TemperaturePlausibilityMin = 0.0, TemperaturePlausibilityMax = 150.0;


struct Temp {
  float Temperature = 40000.0;
  float CAN_Temperature = 40000.0;
  float RS485_Temperature = 40000.0;
  unsigned int QoS = 65534;           /* 1 - OK, 2 - CAN Error, 3 - RS485 Error, Any other - both wrong*/
  unsigned int CAN_QoS = 65534;       /* 1 - OK, Any other - CAN Error*/
  unsigned int RS485_QoS = 65534;       /* 1 - OK, Any other - RS485 Error*/
  unsigned int CAN_timeout = 0;
  unsigned int RS485_timeout = 0;
  byte CAN_PrevMsgCounter = 255;
  byte RS485_PrevMsgCounter = 255;
  RtcDateTime CAN_TimeStamp = Default_TimeStamp;
  RtcDateTime RS485_TimeStamp = Default_TimeStamp;
  unsigned int CAN_ErrorState = 0;                          /* 0 - default, 1 - active error occurence, 2 - previous error occurence*/
  RtcDateTime CAN_ErrorTimeStamp = Default_TimeStamp;
  unsigned int RS485_ErrorState = 0;                        /* 0 - default, 1 - active error occurence, 2 - previous error occurence*/
  RtcDateTime RS485_ErrorTimeStamp = Default_TimeStamp;  
};
/* The N is storing the previous state. If it is different then display it*/
struct Temp T1;
struct Temp T1N;
struct Temp T2;
struct Temp T2N;
struct Temp T3;
struct Temp T3N;
struct Temp T4;
struct Temp T4N;
struct Temp T_Min;
struct Temp T_Max;



struct Flw {
  float Flow = 40000.0;
  float CAN_Flow = 40000.0;
  float RS485_Flow = 40000.0;
  unsigned int QoS = 65534; /* 1 - OK, 2 - CAN Error, 3 - RS485 Error, Any other - both wrong*/
  unsigned int CAN_QoS = 65534;       /* 1 - OK, Any other - CAN Error*/
  unsigned int RS485_QoS = 65534;       /* 1 - OK, Any other - RS485 Error*/  
  unsigned int CAN_timeout = 0;
  unsigned int RS485_timeout = 0;
  byte CAN_PrevMsgCounter = 0;
  byte RS485_PrevMsgCounter = 0;
  RtcDateTime CAN_TimeStamp = Default_TimeStamp;
  RtcDateTime RS485_TimeStamp = Default_TimeStamp;
  unsigned int CAN_ErrorState = 0;                          /* 0 - default, 1 - active error occurence, 2 - previous error occurence*/
  RtcDateTime CAN_ErrorTimeStamp = Default_TimeStamp;
  unsigned int RS485_ErrorState = 0;                        /* 0 - default, 1 - active error occurence, 2 - previous error occurence*/
  RtcDateTime RS485_ErrorTimeStamp = Default_TimeStamp;  
};
/* The N is storing the previous state. If it is different then display it*/
struct Flw F3;
struct Flw F3N;
struct Flw F4;
struct Flw F4N;



struct Valve {
  int State = 9999; /* 1 - Boiler selected, 2 - Buffer selected, Any other - error*/
  unsigned int QoS = 65534; /* 1 - OK, anything other - wrong*/
  unsigned int timeout = 0;
};

/* The N is storing the previous state. If it is different then display it*/
struct Valve V1;
struct Valve V1N;
struct Valve V2;
struct Valve V2N;


struct Pump {
  int State = 2; /* 0 - stopped, 1 - running, 2 - error*/
  unsigned int QoS = 65534; /* 1 - OK, anything other - wrong*/
  unsigned int timeout = 0;
};
/* The N is storing the previous state. If it is different then display it*/
struct Pump P1;
struct Pump P1N;


struct Relay {
  int State = 2; /* 0 - off, 1 - on, 2 - error*/
  unsigned int QoS = 65534; /* 1 - OK, anything other - wrong*/
  unsigned int timeout = 0;
};

struct Relay PVHeating;
struct Relay PVHeatingN;


struct SwtchIn {
  int State = 0;
  int QoS = 0;
};

struct SwtchIn AutManStatus;
struct SwtchIn PufferHeatingSelectorStatus;




void setup() {
    
    Serial.begin(115200);
    Serial2.begin(115200);
    
    /*********************** watchdog setup **************************************************/
    wdt_disable();/* diable the watchdog and wait for more than 2 seconds */
    delay(3000); /* Done so the Arduino doesnot keep resetting infinitely in case of wrong configuration*/
    wdt_enable(WDTO_4S); /* Enable the watchdog with a timeout of 2 seconds */

    pinMode(HeatSourceBoilerInputPin, INPUT_PULLUP);
    pinMode(HeatSourceBufferInputPin, INPUT_PULLUP);
    pinMode(V1BoilerInputPin, INPUT_PULLUP);
    pinMode(V1BufferInputPin, INPUT_PULLUP);
    pinMode(PumpRelayInputPin, INPUT_PULLUP);
    pinMode(ThermostatInputPin, INPUT_PULLUP);
    pinMode(PumpManualModeStartInputPin, INPUT_PULLUP);
    pinMode(PVManualModeHeatingInputPin, INPUT_PULLUP);
    pinMode(AutoModeInputPin, INPUT_PULLUP);
    pinMode(ManModeInputPin, INPUT_PULLUP);
    pinMode(ManModeBoilerSelectionInputPin, INPUT_PULLUP);
    pinMode(ManModeBufferSelectionInputPin, INPUT_PULLUP);
    pinMode(ScreenSelectorButtonPin, INPUT_PULLUP);
    pinMode(PVHeatingRelayInputPin, INPUT_PULLUP);


    pinMode(HeatSourceSelectorOutputPin, OUTPUT);
    pinMode(ControllerWorkingOutputPin, OUTPUT);
    pinMode(PumpStartOutputPin, OUTPUT);
    pinMode(BoilerCircuitOutputPin, OUTPUT);
    pinMode(PVHeatingOutputPin, OUTPUT);
    pinMode(PVHeatingSSROutputPin, OUTPUT);
    pinMode(AutoModeLEDOutputPin, OUTPUT);

/*    digitalWrite(HeatSourceSelectorOutputPin, HIGH);
    digitalWrite(ControllerWorkingOutputPin, HIGH);
    digitalWrite(PumpStartOutputPin, LOW);    
    digitalWrite(BoilerCircuitOutputPin, HIGH);
    digitalWrite(PVHeatingOutputPin, HIGH);
    digitalWrite(PVHeatingSSROutputPin, HIGH);
    digitalWrite(AutoModeLEDOutputPin, HIGH);
*/
    // Set the DE and RE pins as outputs
    pinMode(DE, OUTPUT);
    pinMode(RE, OUTPUT);
  
    // Set DE and RE LOW to disable transmission mode
    digitalWrite(DE, LOW);
    digitalWrite(RE, LOW);

    Serial3.begin(115200);
    Serial2.begin(115200);

    SentcanMsg.can_id  = 0x000;
    SentcanMsg.can_dlc = 8;
    SentcanMsg.data[0] = 0x00;
    SentcanMsg.data[1] = 0x00;
    SentcanMsg.data[2] = 0x00;
    SentcanMsg.data[3] = 0x00;
    SentcanMsg.data[4] = 0x00;
    SentcanMsg.data[5] = 0x00;
    SentcanMsg.data[6] = 0x00;
    SentcanMsg.data[7] = 0x00;

    RecvcanMsg.can_id  = 0x110;
    RecvcanMsg.can_dlc = 8;
    RecvcanMsg.data[0] = 0x00;
    RecvcanMsg.data[1] = 0x00;
    RecvcanMsg.data[2] = 0x00;
    RecvcanMsg.data[3] = 0x00;
    RecvcanMsg.data[4] = 0x00;
    RecvcanMsg.data[5] = 0x00;
    RecvcanMsg.data[6] = 0x00;
    RecvcanMsg.data[7] = 0x00;

    mcp2515.reset();
    mcp2515.setBitrate(CAN_500KBPS);
    mcp2515.setNormalMode();

    // initialize the RTC
    Serial.print("compiled: ");
    Serial.print(__DATE__);
    Serial.println(__TIME__);
    Serial.println("RTC begin: ");
    Rtc.Begin();
    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    Serial.print("Compile time: ");   
    printDateTime(compiled);
    Serial.println("Setting the RTC");  
    Rtc.SetDateTime(compiled);
    if (!Rtc.IsDateTimeValid()) 
    {
        // Common Causes:
        //    1) first time you ran and the device wasn't running yet
        //    2) the battery on the device is low or even missing
        Serial.println("RTC lost confidence in the DateTime!");        
    }

    if (Rtc.GetIsWriteProtected())
    {
        Serial.println("RTC was write protected, enabling writing now");
        Rtc.SetIsWriteProtected(false);
    }

    if (!Rtc.GetIsRunning())
    {
        Serial.println("RTC was not actively running, starting now");
        Rtc.SetIsRunning(true);
    }

    lcd.init(); // initialize the lcd
    lcd.backlight();
    lcd.setCursor(2, 1);
    lcd.print("Controller Startup");

    Serial.println("Controller Startup");
}

void loop(){
//Serial.println("Input reading");  
  // Reading the inputs
  digitalInputReading(HeatSourceBoilerInputPin, &HeatSourceBoilerInput, &HeatSourceBoilerInputRisingEdge, first_execution);
/*Serial.print("Heat source boiler input: ");
Serial.print(HeatSourceBoilerInput);*/
  digitalInputReading(HeatSourceBufferInputPin, &HeatSourceBufferInput, &HeatSourceBufferInputRisingEdge, first_execution);
/*Serial.print(", Heat source buffer input: ");
Serial.println(HeatSourceBufferInput);*/
  digitalInputReading(V1BufferInputPin, &V1BufferInput, &V1BufferInputRisingEdge, first_execution);
  digitalInputReading(V1BoilerInputPin, &V1BoilerInput, &V1BoilerInputRisingEdge, first_execution);
/*  digitalInputReading(V2BufferInputPin, &V2BufferInput, &V2BufferInputRisingEdge, first_execution);
  digitalInputReading(V2BoilerInputPin, &V2BoilerInput, &V2BoilerInputRisingEdge, first_execution); */
  digitalInputReading(PumpRelayInputPin, &PumpRelayInput, &PumpRelayInputRisingEdge, first_execution);
/*Serial.print("Pump start input: ");
Serial.println(PumpRelayInput);*/
  digitalInputReading(ThermostatInputPin, &ThermostatInput, &ThermostatInputRisingEdge, first_execution);
  HeatingRequest = !ThermostatInput;
/*Serial.print("HeatingRequest: ");
Serial.println(HeatingRequest);*/
  digitalInputReading(PumpManualModeStartInputPin, &PumpManualModeStartInput, &PumpManualStartInputRisingEdge, first_execution);
  digitalInputReading(PVHeatingRelayInputPin, &PVHeatingRelayInput, &PVHeatingRelayInputRisingEdge, first_execution);
/*Serial.print("Pump start input: ");
Serial.println(PumpManualModeStartInput);*/
  digitalInputReading(PVManualModeHeatingInputPin, &PVManualModeHeatingInput, &PVManualHeatingInputRisingEdge, first_execution);
  digitalInputReading(ScreenSelectorButtonPin, &ScreenSelectorButtonState, &ScreenSelectorButtonRisingEdge, first_execution);
/*Serial.print("Screenselector start input: ");
Serial.println(ScreenSelectorButtonState);*/
  SwitchInputReading(&AutManStatus, AutoModeInputPin, ManModeInputPin);
  SwitchInputReading(&PufferHeatingSelectorStatus, ManModeBoilerSelectionInputPin, ManModeBufferSelectionInputPin);

//Serial.println("RTC reading");  
  // get the current time
  RtcDateTime now = Rtc.GetDateTime();
  actSecond = now.Second();
  act_Min = now.Minute();
  act_Hour = now.Hour();
  act_Month = now.Month();
 
  // Set DE and RE LOW to disable transmission mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);  
  RS485MsgReceiving();
  CANMsgReceiving();
  CAN_RS485_MsgTimeOutCheck();
  SignalAvailabilityCheckAndSelection();
  AutManState();
  HeatingAlgorithm();
  PVHeatingDelays();
  ValveControl();    
  PumpControl();
  CurrentValueReceiving();

  

  Output_Writing(HeatSource, PumpCommand, PVHeatingRelayOutput, PVHeatingSSROutput, AutoModeLEDOutput, ControllerWorking);
/*Serial.print("Pump start command: ");
Serial.println(PumpCommand);*/





  if(previousSec != actSecond){
      DisplayHandling();
      RS485MsgSending();
      CANMsgSending();
  }

  /* Since there is more responsiobility on the RTC clock, it shall be checked frequently.
   * If the RTC is not valid, then try to reset the controller and init the RTC*/
  if((previousMin != act_Min) && (!Rtc.IsDateTimeValid())){
    lcd.clear();
    lcd.setCursor(5, 1);
    lcd.print("RTC Error, resetting the controller");
    while(1){}           
          
  }

  ChangeUpdate();
  TempAvgCounting();
  PVHeatingCheck();
  PumpStartingCheck();

  wdt_reset(); /* Reset the wdt timer */

  /*increment the execution number*/  
  exectuion_number++;
  /*compare it with a limit and set the variable*/
  if(exectuion_number > 3){
    first_execution = 1;
  } else {
    first_execution = 0;
  }
  
}


void AutManState(){
  /*If the State is 2 and QoS is okay, then select auto mode*/
/*Serial.print("QoS: ");
Serial.print(AutManStatus.QoS);
Serial.print("State ");
Serial.println(AutManStatus.State);  */

  if((AutManStatus.QoS == 1)&&(AutManStatus.State == 2)){
      AutoManState = 1;
      AutoModeLEDOutput = 1;
//Serial.println("Aut Mode");

    /*This else if might not be needed since if the QoS is not okay, manual mode
     * is selected but for debugging it might be useful*/
  } else if((AutManStatus.QoS == 1)&&(AutManStatus.State == 1)){
      AutoManState = 0;
      AutoModeLEDOutput = 0;
//Serial.println("Man Mode");     

  } else {
      AutoManState = 0;
      AutoModeLEDOutput = 0;

  }
  
}

/************************ Checking the valve ***********************************************/
void ValveControl(){

  /* Checking the Valve statuses one by one. */
  ValveCheck(HeatSource, HeatSourceBoilerInput, HeatSourceBufferInput, V1BufferInput, V1BoilerInput, ValveTimoutErrorLimit, &V1);
  ValveCheck(HeatSource, HeatSourceBoilerInput, HeatSourceBufferInput, V1BufferInput, V1BoilerInput, ValveTimoutErrorLimit, &V2);  
/*Serial.print("HeatSourceBoilerInput: ");
Serial.print(HeatSourceBoilerInput);
Serial.print(", HeatSourceBufferInput: ");
Serial.print(HeatSourceBufferInput);
Serial.print(", Heat source: ");
Serial.print(HeatSource);
Serial.print(", V1 State: ");
Serial.print(V1.State);
Serial.print(", V1 QoS: ");
Serial.print(V1.QoS);*/

/*The ValveDelayElapsed shall be set to zero */
  if(HeatSource != HeatSource_N){
      ValveDelayElapsed = 0;
      HeatSource_N = HeatSource;
  }

  /*Checking the valve for Boiler settings or Buffer setting*/
  if(HeatSource && HeatSourceBoilerInput && !HeatSourceBufferInput && (V1.State == 1) && (V1.QoS == 1) && ValveDelayElapsed){
    ValveSelection = 1;
    ValveErrorCounter = 0;
    ValveError = 0;
/*Serial.print("Valve Selection: ");
Serial.print(ValveSelection);
Serial.println(" , Buffer selected: ");
*/
  } else if(!HeatSource && !HeatSourceBoilerInput && HeatSourceBufferInput && (V1.State == 2) && (V1.QoS == 1) && ValveDelayElapsed){
    ValveSelection = 2;
    ValveErrorCounter = 0;
    ValveError = 0;
/*Serial.print(", Valve Selection: ");
Serial.print(ValveSelection);
Serial.println(" , Boiler selected: ");
*/
  } else {
    /* If the valve is not set then give some time before the error reporting*/
    /*currently the same timeout is used here and for the individual valve timeout. If there is an issue they shall be separated.*/
 //Serial.print(", Valve error: ");   
    if(previousSec != actSecond){
        ValveErrorCounter++;
    }

    if(ValveDelay < ValveErrorCounter){
        ValveDelayElapsed = 1;

    }



    if(ValveErrorCounter > ValveTimoutErrorLimit){
        ValveSelection = 9999;
        ValveError = 1;
        
    } else {
        ValveSelection = 0;

    }

/*Serial.print(ValveSelection);  
Serial.print(", Valve selection error: "); 
Serial.println(ValveError);
Serial.print("Valve Delay: ");
Serial.print(ValveDelayElapsed);*/
  }

}

/*Since the valves have different states depending on the heating source selection the valve check shall be done on Boiler and Buffer*/
void ValveCheck(int HeatSource, int HeatSourceBoilerInput, int HeatSourceBufferInput, int ValveBuffer, int ValveBoiler, int ValveTimeout, struct Valve *ValveIn){
  
  /*The valves need approx 6s for position change. For that a delay shall be implemented and if that elapsed the valve state set.*/

  if(HeatSource && ValveBoiler && !ValveBuffer){
    /*If the buffer is selected then the valve shall be in that position*/
    ValveIn->State = 1;
    ValveIn->QoS = 1;
    ValveIn->timeout = 0;

  } else if(!HeatSource && !ValveBoiler && ValveBuffer){
    /*If the boiler is selected then the valve shall be in that position*/
    ValveIn->State = 2;
    ValveIn->QoS = 1;
    ValveIn->timeout = 0;

  } else {
    /*If none of these fulfilled, after a timeout an error shall be reported*/
    if(previousSec != actSecond){
        ValveIn->timeout++;
    }

    if(ValveTimeout < ValveIn->timeout){
        ValveIn->State = 9999;
        ValveIn->QoS = 0;

    }
  }
}
/************************ end of Checking the valve ***********************************************/




/* The pump shall be started based on a request coming from the heating control. The pump control shall check all the conditions for starting the pump.
 * If the pump is started all the conditions shall be monitored for keeping it running.*/
void PumpControl(){

  switch (PumpControlState){
    case PumpNotRunning:
          /* Starting the pump if there is a heating or circulation request. The valves can be in Buffer tank or Boiler position*/
/*Serial.print("Pump not running state"); 
Serial.print(", Pump start request: ");
Serial.println(PumpStartRequest);*/
          if(PumpStartRequest && ((ValveSelection == 1)||(ValveSelection == 2))){
              PumpCommand = 1;
              PumpControlState = PumpStarting;
              P1.State = 1;
              P1.QoS = 1;
//Serial.println("Pump starting");              
          }         
          PumpError = 0;
      break;

    case PumpStarting:   
            /* If the pump has been started the feedback of the starting relay shall be checked. if it is not received in time, then the 
            * pump shall be stopped and the error state is set.*/
//Serial.print("Pump starting state");             
            if(previousSec != actSecond){
                PumpErrorCounter++;
            }
            
            if(PumpErrorCounter > PumpErrorLimit){
                PumpCommand = 0;
                PumpControlState = PumpErrorState;

            }
/*Serial.print("Pump error counter: ");
Serial.println(PumpErrorCounter);           
Serial.print("Pump feedback: ");
Serial.println(PumpRelayInput);*/
            /* The NO of the relay shall be read. If it is okay, then set the pump running state
             * The pump startup limit shall be checked to allow the relay to settle(get rid of the bouncing)
             * and also allow the flow to built up, The error counter is used as a second counter.*/
            if(PumpRelayInput && (PumpErrorCounter > PumpStartupCounter)){
/*Serial.print("Pump error counter: ");
Serial.print(PumpErrorCounter);*/
                PumpErrorCounter = 0;
                PumpControlState = PumpRunning;

            }
      break;

    case PumpRunning:
          /* If any of the running conditions which are required for running: valve and the relay feedback or 
           * the flow is below any limit, are not fullfilled then the error state shall be set.
           * If the heating request conditions is gone, then the pump shall be stopped.*/
//Serial.println("Pump running state"); 
           if(((ValveSelection != 1) && (FlowLowLimit > F3.Flow)&&(F3.QoS == 1))||(!PumpRelayInput && PumpStartRequest)||((ValveSelection != 2)&& (FlowLowLimit > F4.Flow)&&(F4.QoS == 1))){
                PumpCommand = 0;
                PumpControlState = PumpErrorState;
                P1.State = 2;
                P1.QoS = 0;
//Serial.println("Running conditions ceased");
           }
           
           if(!PumpStartRequest){
                PumpCommand = 0;
                PumpControlState = PumpNotRunning;
                P1.State = 0;
                P1.QoS = 1;
//Serial.println("Running conditions stopped");
           }
     
      break;

    case PumpErrorState:
        /* If the pump is in error state, it could be reset if the pump is started and the relay feedback
         * and the produced flow are okay*/
/*Serial.print("Pump error state, ");
Serial.print("Error:");
Serial.print(PumpError);
Serial.print(", Qos:");
Serial.println(P1.QoS);*/

          if((PumpStartRequest && !PumpRelayInput) && ((ValveSelection == 1)||(ValveSelection == 2))){
              PumpCommand = 1;
              P1.State = 1;
/*Serial.println("Manual starting, cmd:");
Serial.println(PumpCommand);*/

          } else if((PumpRelayInput && PumpStartRequest)){  /* Later if the flow measurement is okay, this shall be put back into the condition: ||((FlowLowLimit < F3.Flow)&&(F3.QoS == 1))*/
              PumpError = 0;
              P1.QoS = 1;
//Serial.println("Error state reset");

          } else if(!PumpStartRequest && (PumpError == 0) && (P1.QoS == 1)){
              PumpControlState = PumpNotRunning;
              PumpCommand = 0;
              P1.State = 0;
              P1.QoS = 1;
//Serial.println("State machine to not running");

          } else {
              PumpCommand = 0;
//Serial.println("No pump cmd");
          }

      break;
  }
  
}


void HeatingAlgorithm(){
  float Local_T = 0.0;

  /* Count the average of the temperature inside the tank. T2 is located at the top, T1 is at the bottom, if both are
   * above the temperature then the tank is heated to the goal temperature.
   * If T1 is missing/ wrong QoS then the T2 shall be considered, if T2 is missing then T1.
   * If none of them is okay, then the Boiler shall be selected as a safe state*/
/*Serial.print("T1 QoS: ");
Serial.print(T1.QoS);
Serial.print(", T2 QoS: ");
Serial.print(T2.QoS);*/
   if((((T1.QoS == 1)||(T1.QoS == 2)||(T1.QoS == 3))&&((T2.QoS == 1)||(T2.QoS == 2)||(T2.QoS == 3)))){
      Local_T = (T1.Temperature + T2.Temperature) / 2;
//Serial.println(", T1 and T2 are okay");

   } else if(((T1.QoS == 1)||(T1.QoS == 2)||(T1.QoS == 3))&&((T2.QoS != 1)||(T2.QoS != 2)||(T2.QoS != 3))){
      Local_T = T1.Temperature;
//Serial.println(", T1 is okay");

   } else if(((T1.QoS != 1)||(T1.QoS != 2)||(T1.QoS != 3))&&((T2.QoS = 1)||(T2.QoS = 2)||(T2.QoS = 3))){
      Local_T = T2.Temperature;
//Serial.println(", T2 is okay");      

   } else {
      HeatSource = 0;
      PVHeatingOutput = 0;
      PumpStartRequest = 0;
//Serial.println("T1 and T2 are wrong");
      return;

   }

     /* If there is a heating request from the thermostat it shall be put into another variable
      * and if there is a problem this variable shall be reset.*/
    /* If the temp in the buffer tank is above the limit, then select it as a heat source*/
    /* Switch status 1 - Manual mode, 2 - Automatic mode*/
 /*Serial.print("Local_T: ");
 Serial.print(Local_T);
/*Serial.print(", Aut Man: ");
Serial.println(AutoManState);*/
    if(((Local_T > BufferTankTempLowLimit)&&(AutoManState))||(!AutoManState && (PufferHeatingSelectorStatus.QoS == 1) && (PufferHeatingSelectorStatus.State == 2))){
          HeatSource = 1;
          BoilerCircuitOutput = 1;
//Serial.println(", Buffer Selected, ");

     } else {
          HeatSource = 0;
          BoilerCircuitOutput = 0;
//Serial.println(", Boiler Selected, ");
     }


/*If it is not heating season the buffer shall be heated up to max temperature and then the heating switched off.
     * There is no need for additional electrical heating.*/
  if((act_Month > 4) && (act_Month < 10)){
    HeatingSeason = false;
//    Serial.println("Not a heating season");
    
  } else {
    /* If it is heating season the buffer tank heating is implemented in the state machine.*/
    HeatingSeason = true;
//    Serial.println("Heating season");  

  }


  /*the system shall enter into auto state if it is heating season. If it is not heating season, the buffer's heating is automatic
   * and if there is a heating request it will start the pump*/
  if(HeatingSeason && AutoManState && HeatSource){
    switch(HeatingState){

      case HeatingBefore2pm: {
Serial.println("HeatingBefore2pm");

          /*It is between 7 am and 2 pm. The pump shall be started for 10 mins if the tank is warm enough*/
          if((act_Min >= 25) && (act_Min < 35) && (T1.Temperature > 40.0)){
Serial.println("Starting the pump");
            PumpStartRequest = 1;

          } else {
Serial.println("Stopping the pump");
            PumpStartRequest = 0;

          }

          /* The PV heating shall be switched on if the temperature is lower than the limit*/
          if(T1.Temperature < BufferTankTempLowLimit){
            PVHeatingOutput = 1;

          } else {
            PVHeatingOutput = 0;

          }


        /* Changing the state to heating after 2pm */
          if((act_Hour >= 14) && (act_Hour < 22)){
              HeatingState = HeatingAfter2pm;
              PVHeatingMinutes = 0;

          }

        break;
      } 

      case HeatingAfter2pm: {
Serial.println("HeatingAfter2pm");

            /*It is between 2 pm and 10 pm. The pump shall be started for 10 mins if the tank is warm enough T > 50.0 C,
             * If the tank is around 40C then it shall change to circulating in two hours to prioritzing the heating the tank for the night showers*/
            if((act_Min >= 25) && (act_Min < 35) && (T1.Temperature > 50.0)){
Serial.println("Starting the pump");
                PumpStartRequest = 1;


          } else if ((T1.Temperature >= 40.0) && (T1.Temperature < 50.0) && (act_Hour % 2) && (act_Min >= 25) && (act_Min < 35)){
Serial.println("Starting the pump");
                PumpStartRequest = 1;

          } else {
Serial.println("Stopping the pump");
                PumpStartRequest = 0;

          }

          /* The PV heating shall be switched on if the temperature is lower than the limit and it is before 4 pm*/
          if((T1.Temperature < BufferTankTempLowLimit) && (act_Hour < 16)){
            lcd.setCursor(10, 1);
            lcd.print("PV On");

          /* If it is after 4 pm. There is a two hour time window to heat the buffer tank since in one hour with electrical heating
          * the temperature can be increased with 10C.
          * */
          } else if((act_Hour >= 16) && ((abs(PVHeatingTemperatureMin - T1.Temperature)) <= TemperatureDifference) && (PVHeatingTemperatureMin > T1.Temperature ) && (T1.Temperature > ElectricalHeatingMinTemp) && (PVHeatingMinutesLimit > PVHeatingMinutes)){
              PVHeatingOutput = 1;

              /*Calculating the time limit for the PVHeating*/
              PVHeatingMinutesLimit = (unsigned int)((abs(PVHeatingTemperatureMin - T1.Temperature) / 10.0) * 60.0);
              if(act_Min != previousMin){
                  PVHeatingMinutes++;

              }


          } else {
              PVHeatingOutput = 0;

          }

          /* Changing the state to heating after 10pm */
          if((act_Hour < 7) || (act_Hour >= 22)){
              HeatingState = HeatingAfter10pm;
              PVHeatingMinutes = 0;
              PVHeatingMinutesLimit = 2;

          }

        break;
      } 

      case HeatingAfter10pm: {
          /* In this state the circulation shall continue as long as there is enough heat in the tank.
           * There is no PV heating in this state.*/
Serial.println("HeatingAfter10pm");
          
          if((act_Min >= 25) && (act_Min < 35) && (T1.Temperature > 25.0) && (act_Hour % 2)){
              Serial.println("Starting the pump");
              PumpStartRequest = 1;

          } else {
              Serial.println("Stopping the pump");
              PumpStartRequest = 0;

          }

            /* Changing the state to heating before 2pm */
            if((act_Hour >= 7) && (act_Hour < 14)){
              HeatingState = HeatingBefore2pm;

            }
        break;
      } 
      
      /* In the default place the actual hour shall be decided*/
      default:{

        if((act_Hour >= 7) && (act_Hour < 14)){
          HeatingState = HeatingBefore2pm;

        } else if((act_Hour >= 14) && (act_Hour < 22)){
          HeatingState = HeatingAfter2pm;

        } else if((act_Hour < 7) || (act_Hour >= 22)){
          HeatingState = HeatingAfter10pm;

        }
        PVHeatingMinutes = 0;
      }
    }

  } else {
    /* If it is not heating season just heating the tank to the limit, without electrical power from the network*/  
    /* If the buffer tank temperature is lower thean the limit, then switch on the heating. */            
          if(((T1.Temperature < BufferTankTempHighLimit) && (act_Hour > 7) && (act_Hour < 18) && (AutoManState)) || ((!AutoManState) && !PVManualModeHeatingInput)){         
            PVHeatingOutput = 1;

          } else {
            PVHeatingOutput = 0;

          }

        if(HeatingRequest && (Local_T > BufferTankTempLowLimit) && (HeatSource == 1) && (AutoManState)){
          /*If in auto mode there is a heating request then the pump shall be started only if the buffer tank is selected for the heating source.*/
            PumpStartRequest = 1;
    //Serial.println("P1 ON: Heating req");

        } else if((!AutoManState)&&!PumpManualModeStartInput){
          /* In manual mode if there is a pump starting command then start the pump*/
            PumpStartRequest = 1;
    //Serial.println("P1 Man running");        

        } else {
            PumpStartRequest = 0;
    //Serial.println("P1 Stop");

        }

  }

}




/* If the temperature in the boiler rises above the safety limit, then the relay shall be opened
 * If no temperature data is available then it shall be opened too.
 * If the relay was opened, it shall be switched back with a hysteresis. */
void PVHeatingCheck(){
      float Local_T = 0.0;

    if((((T1.QoS == 1)||(T1.QoS == 2)||(T1.QoS == 3))&&((T2.QoS == 1)&&(T2.QoS == 2)||(T2.QoS == 3)))){
      Local_T = (T1.Temperature + T2.Temperature) / 2;

    } else if(((T1.QoS == 1)||(T1.QoS == 2)||(T1.QoS == 3))&&((T2.QoS != 1)&&(T2.QoS != 2)&&(T2.QoS != 3))){
        Local_T = T1.Temperature;

    } else if(((T1.QoS != 1)&&(T1.QoS != 2)&&(T1.QoS != 3))&&((T2.QoS != 1)||(T2.QoS != 2)||(T2.QoS != 3))){
        Local_T = T2.Temperature;

    } else {
        Local_T = 0.0;

    }

    if(((T1.QoS == 1)||(T1.QoS == 2)||(T1.QoS == 3))||((T2.QoS == 1)||(T2.QoS == 2)||(T2.QoS == 3))){
        
        if(Local_T > BufferTankTempHighLimit){
          PVHeatingOutput = 0;

        } else if(Local_T < BufferTankTempLowLimit){
          PVHeatingOutput = PVHeatingOutput;

        }
      
    }  else {
        PVHeatingOutput = 0;

    }

}

/*The relay and the solid state relay signal shall be delayed as the following:
 * - at switching on the SSR shall be delayed as long as the feedback signal is provided by the PV magnetic switch to
 *   protect the contactor of the magnetic switch
 * - at switch off the magnetic switch switch shall be delayed with 1 second, to also protect its contactors from burning
 *   
*/
void PVHeatingDelays(){

  /*Rising edge detection*/  
  if(PVHeatingOutput && !PVHeatingOutputN1){
    PVHeatingOutputRisingEdge = 1;
    PVHeatingOutputFallingEdge = 0;
    PVHeatingOutputN1 = PVHeatingOutput;
    PVHeatingSSROutputT_ONDelayCounter = 0;
    PVHeatingRelayOutput = 1;
  }

  /*Fallng edge detection*/  
  if(!PVHeatingOutput && PVHeatingOutputN1){
    PVHeatingOutputRisingEdge = 0;
    PVHeatingOutputFallingEdge = 1;
    PVHeatingOutputN1 = PVHeatingOutput;
    PVHeatingRelayOutputT_OFFDelayCounter = 0;
    PVHeatingSSROutput = 0;
  }

  if(previousSec != actSecond){
    
    if(PVHeatingOutputRisingEdge){
      PVHeatingSSROutputT_ONDelayCounter++;

      if(PVHeatingSSROutputT_ONDelayCounter > PVHeatingSSROutputT_ONDelay){
        PVHeatingSSROutput = 1;
        PVHeatingOutputRisingEdge = 0;
      }
    }

    if(PVHeatingOutputFallingEdge){
      PVHeatingRelayOutputT_OFFDelayCounter++;

      if(PVHeatingRelayOutputT_OFFDelayCounter > PVHeatingRelayOutputT_OFFDelay){
        PVHeatingRelayOutput = 0;
        PVHeatingOutputFallingEdge = 0;
      }
    }    

  }


}


/* The pump starting shall only be available if the V1 has reached any of its end positions*/
...

This file has been truncated, please download it to see its full contents.

CheckerCode

C/C++
#include <avr/wdt.h>
#include <SPI.h>
#include <mcp2515.h>
#include "CRC.h"
#include <Arduino.h>
#include <RtcDS1302.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20 column and 4 rows

struct can_frame RecvcanMsg;
struct can_frame SentcanMsg;
MCP2515 mcp2515(53);

#define DE 3
#define RE 2

#define BUFFER_SIZE 64   // maximális üzenethossz
char DataToBeProcessed[BUFFER_SIZE];
uint8_t dataIndex = 0;

// DS1302 RTC instance
// CONNECTIONS:
// DS1302 CLK/SCLK --> 5
// DS1302 DAT/IO --> 4
// DS1302 RST/CE --> 2
// DS1302 VCC --> 3.3v - 5v
// DS1302 GND --> GND
ThreeWire myWire(48,49,47); // IO, SCLK, CE
RtcDS1302<ThreeWire> Rtc(myWire);

/*********** global variables *************/
unsigned long exectuion_number = 0, ControllerMsg_TimeOut = 0, ControllerMsgTimeoutLimit = 4;
bool ControllerTimeoutOK = false;
/* CAN */
int first_execution = 0, previousSec = 0, actSecond = 0, previousMin = 0, actMin = 0, CANMsgTimeout = 35, RS485MsgTimeout = 38, SerialResetErrorCounter = 1, CANResetErrorCounter = 1;
float BufferTankTempLowLimit = 70.0, BufferTankTempHighLimit = 85.0, TempDifferentInsisdeBuffertank = 5.0;
int PumpCommand = 0;
int HeatSource = 0;  /* 0 - Boiler, 1 - Buffer tank*/
unsigned int ValveTimeOut = 0, ValveTimeOutLimit = 15;
/**************** Digital inputs and outputs *******************/
int HeatSourceBoilerInputPin = 35, HeatSourceBoilerInput = 0, HeatSourceBoilerInputRisingEdge = 0, HeatSourceBufferInputPin = 36, HeatSourceBufferInput = 0, HeatSourceBufferInputRisingEdge = 0; 
int V1BufferInputPin = 40, V1BufferInput = 0, V1BufferInputRisingEdge = 0, V1BoilerInputPin = 41, V1BoilerInput = 0, V1BoilerInputRisingEdge = 0;
int V2BufferInputPin = 40, V2BufferInput = 0, V2BufferInputRisingEdge = 0, V2BoilerInputPin = 41, V2BoilerInput = 0, V2BoilerInputRisingEdge = 0;
int PumpStartOutputPin = 10, PumpRelayInputPin = 38, PumpRelayInput = 0, PumpRelayInputRisingEdge = 0;
int ThermostatInputPin = 0, ThermostatInput = 0, ThermostatInputRisingEdge = 0;
int BoilerCircuitOutputPin = 0, BoilerCircuitOutput = 0;
int PVManualModeHeatingInputPin = 42, PVManualModeHeatingInput = 0, PVManualHeatingInputRisingEdge = 0;
int PVHeatingOutput = 0, PVHeatingOutputN1 = 0, PVHeatingOutputRisingEdge = 0, PVHeatingOutputFallingEdge = 0;
int PVHeatingSSROutputT_ONDelay = 2, PVHeatingSSROutputT_ONDelayCounter = 0, PVHeatingRelayOutputT_OFFDelay = 2, PVHeatingRelayOutputT_OFFDelayCounter = 0; 
int PVHeatingSSROutputPin = 9, PVHeatingSSROutput = 0, PVHeatingOutputPin = 11, PVHeatingRelayOutput = 0;
int RedLightOutputPin = 34, RedLightOutput = 0;
int YellowLightOutputPin = 33, YellowLightOutput = 0;
int GreenLightOutputPin = 32, GreenLightOutput = 0;

byte MsgCountN = 0;

byte CRCcalc[8];

char defaultDate[10] = {"1987:03:11"};

char defaultTime[8] = {"04:30:00"};

RtcDateTime Default_TimeStamp = RtcDateTime(defaultDate, defaultTime);

float TemperatureChangeLimit = 10.0, TemperaturePlausibilityMin = 0.0, TemperaturePlausibilityMax = 150.0;

struct Temp {
  float Temperature = 40000.0;
  float CAN_Temperature = 40000.0;
  float RS485_Temperature = 40000.0;
  unsigned int QoS = 65534;           /* 1 - OK, 2 - CAN Error, 3 - RS485 Error, Any other - both wrong*/
  unsigned int CAN_QoS = 65534;       /* 1 - OK, Any other - CAN Error*/
  unsigned int RS485_QoS = 65534;       /* 1 - OK, Any other - RS485 Error*/
  unsigned int CAN_timeout = 0;
  unsigned int RS485_timeout = 0;
  byte CAN_PrevMsgCounter = 255;
  byte RS485_PrevMsgCounter = 255;
  RtcDateTime CAN_TimeStamp = Default_TimeStamp;
  RtcDateTime RS485_TimeStamp = Default_TimeStamp;
};
/* The N is storing the previous state. If it is different then display it*/
struct Temp T1;
struct Temp T1N;
struct Temp T2;
struct Temp T2N;
struct Temp T3;
struct Temp T3N;
struct Temp T4;
struct Temp T4N;




struct Flw {
  float Flow = 40000.0;
  float CAN_Flow = 40000.0;
  float RS485_Flow = 40000.0;
  unsigned int QoS = 65534; /* 1 - OK, 2 - CAN Error, 3 - RS485 Error, Any other - both wrong*/
  unsigned int CAN_QoS = 65534;       /* 1 - OK, Any other - CAN Error*/
  unsigned int RS485_QoS = 65534;       /* 1 - OK, Any other - RS485 Error*/  
  unsigned int CAN_timeout = 0;
  unsigned int RS485_timeout = 0;
  byte CAN_PrevMsgCounter = 0;
  byte RS485_PrevMsgCounter = 0;
  RtcDateTime CAN_TimeStamp = Default_TimeStamp;
  RtcDateTime RS485_TimeStamp = Default_TimeStamp;
};
/* The N is storing the previous state. If it is different then display it*/
struct Flw F3;
struct Flw F3_Controller;
struct Flw F4;
struct Flw F4_Controller;



struct Valve {
  int State = 2; /* 1 - Boiler selected, 2 - Buffer selected, Any other - error*/
  unsigned int QoS = 65534; /* 1 - OK, anything other - wrong*/
  unsigned int timeout = 0;
};

/* The N is storing the previous state. If it is different then display it*/
struct Valve V1;
struct Valve V2;



struct Pump {
  int State = 2; /* 0 - stopped, 1 - running, 2 - error*/
  unsigned int QoS = 65534; /* 1 - OK, anything other - wrong*/
  unsigned int timeout = 0;
};
/* The N is storing the previous state. If it is different then display it*/
struct Pump P1;
struct Pump P1N;


struct Relay {
  int State = 2; /* 0 - off, 1 - on, 2 - error*/
  unsigned int QoS = 65534; /* 1 - OK, anything other - wrong*/
  unsigned int timeout = 0;
};

struct Relay PVHeating;
struct Relay PVHeatingN;


struct SwtchIn {
  int State = 0;
  int QoS = 0;
};

struct SwtchIn AutManStatus;
struct SwtchIn PufferHeatingSelectorStatus;




void setup() {
        
    /*********************** watchdog setup **************************************************/
    wdt_disable();/* diable the watchdog and wait for more than 2 seconds */
    delay(3000); /* Done so the Arduino doesnot keep resetting infinitely in case of wrong configuration*/
    wdt_enable(WDTO_4S); /* Enable the watchdog with a timeout of 2 seconds */

    pinMode(HeatSourceBoilerInputPin, INPUT_PULLUP);
    pinMode(HeatSourceBufferInputPin, INPUT_PULLUP);
/*    pinMode(V1BufferInputPin, INPUT_PULLUP);
    pinMode(V1BoilerInputPin, INPUT_PULLUP);
*/    pinMode(PumpRelayInputPin, INPUT_PULLUP);
    pinMode(ThermostatInputPin, INPUT_PULLUP);

    pinMode(13, OUTPUT);
    pinMode(PumpStartOutputPin, OUTPUT);
    pinMode(BoilerCircuitOutputPin, OUTPUT);
    pinMode(PVHeatingOutputPin, OUTPUT);
    pinMode(PVHeatingSSROutputPin, OUTPUT);
    pinMode(RedLightOutputPin, OUTPUT);
    pinMode(YellowLightOutputPin, OUTPUT);
    pinMode(GreenLightOutputPin, OUTPUT);

    digitalWrite(13, HIGH);
    digitalWrite(PumpStartOutputPin, LOW);    
    digitalWrite(BoilerCircuitOutputPin, HIGH);
    digitalWrite(PVHeatingOutputPin, HIGH);
    digitalWrite(PVHeatingSSROutputPin, HIGH);

    // Set the DE and RE pins as outputs
    pinMode(DE, OUTPUT);
    pinMode(RE, OUTPUT);
  
    // Set DE and RE LOW to disable transmission mode
    digitalWrite(DE, LOW);
    digitalWrite(RE, LOW);

    Serial.begin(115200);
    Serial3.begin(115200);

    SentcanMsg.can_id  = 0x000;
    SentcanMsg.can_dlc = 8;
    SentcanMsg.data[0] = 0x00;
    SentcanMsg.data[1] = 0x00;
    SentcanMsg.data[2] = 0x00;
    SentcanMsg.data[3] = 0x00;
    SentcanMsg.data[4] = 0x00;
    SentcanMsg.data[5] = 0x00;
    SentcanMsg.data[6] = 0x00;
    SentcanMsg.data[7] = 0x00;

    RecvcanMsg.can_id  = 0x110;
    RecvcanMsg.can_dlc = 8;
    RecvcanMsg.data[0] = 0x00;
    RecvcanMsg.data[1] = 0x00;
    RecvcanMsg.data[2] = 0x00;
    RecvcanMsg.data[3] = 0x00;
    RecvcanMsg.data[4] = 0x00;
    RecvcanMsg.data[5] = 0x00;
    RecvcanMsg.data[6] = 0x00;
    RecvcanMsg.data[7] = 0x00;

    mcp2515.reset();
    mcp2515.setBitrate(CAN_500KBPS);
    mcp2515.setNormalMode();

    // initialize the RTC
    Serial.print("compiled: ");
    Serial.print(__DATE__);
    Serial.println(__TIME__);
    Serial.println("RTC begin: ");
    Rtc.Begin();
    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    Serial.print("Compile time: ");   
    //printDateTime(compiled);
    Serial.println("Setting the RTC");  
    Rtc.SetDateTime(compiled);
    if (!Rtc.IsDateTimeValid()) 
    {
        // Common Causes:
        //    1) first time you ran and the device wasn't running yet
        //    2) the battery on the device is low or even missing
        Serial.println("RTC lost confidence in the DateTime!");        
    }

    if (Rtc.GetIsWriteProtected())
    {
        Serial.println("RTC was write protected, enabling writing now");
        Rtc.SetIsWriteProtected(false);
    }

    if (!Rtc.GetIsRunning())
    {
        Serial.println("RTC was not actively running, starting now");
        Rtc.SetIsRunning(true);
    }

    lcd.init(); // initialize the lcd
    lcd.backlight();

    Serial.println("Checker Startup");
    lcd.setCursor(5, 1);
    lcd.print("Checker Startup");
}

void loop() {
  digitalInputReading(HeatSourceBoilerInputPin, &HeatSourceBoilerInput, &HeatSourceBoilerInputRisingEdge, first_execution);
  digitalInputReading(HeatSourceBufferInputPin, &HeatSourceBufferInput, &HeatSourceBufferInputRisingEdge, first_execution);
  /*digitalInputReading(V1BufferInputPin, &V1BufferInput, &V1BufferInputRisingEdge, first_execution);
  digitalInputReading(V1BoilerInputPin, &V1BoilerInput, &V1BoilerInputRisingEdge, first_execution);
  digitalInputReading(V2BufferInputPin, &V2BufferInput, &V2BufferInputRisingEdge, first_execution);
  digitalInputReading(V2BoilerInputPin, &V2BoilerInput, &V2BoilerInputRisingEdge, first_execution);*/
  digitalInputReading(PumpRelayInputPin, &PumpRelayInput, &PumpRelayInputRisingEdge, first_execution);
  digitalInputReading(ThermostatInputPin, &ThermostatInput, &ThermostatInputRisingEdge, first_execution);

  /* Reading the actual second */
  RtcDateTime now = Rtc.GetDateTime();
  actSecond = now.Second();
  actMin = now.Minute();


  // Set DE and RE LOW to disable transmission mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);
  RS485MsgReceiving();
  CANMsgReceiving();
  CAN_RS485_MsgTimeOutCheck();
  SignalAvailabilityCheckAndSelection();
  PVHeatingCheck();
  PVHeatingDelays();  
  PumpStartingCheck();
  WritingOutputs();
  ChangeUpdate();
  wdt_reset(); /* Reset the wdt timer */


  if(previousSec != actSecond){
      previousSec = actSecond;
      GreenLightOutput =!GreenLightOutput;
      LCDHandling();
  }

  if(previousMin != actMin){
      previousMin = actMin;
      SerialResetErrorCounter = 0;
      CANResetErrorCounter = 0;
      /* If the RTC is not valid, then try to reset the controller and init the RTC*/
      if(!Rtc.IsDateTimeValid()){
        lcd.clear();
        lcd.setCursor(5, 1);
        lcd.print("RTC Error, resetting the controller");
        while(1){} 
      }
  }

  /*initialize the lcd*/
  if(actMin == 57){
    lcd.init(); 
    lcd.backlight();
  }

  /*increment the execution number*/  
  exectuion_number++;
  /*compare it with a limit and set the variable*/
  if(exectuion_number > 3){
    first_execution = 1;
  } else {
    first_execution = 0;
  }

}

/* If the temperature in the boiuler rises above the safety limit, then the relay shall be opened
 * If no temperature data is available then it shall be opened too.
 * If the relay was opened, it shall be switched back with a hysteresis. */
void PVHeatingCheck(){
    float Local_T = 0.0;

    if((((T1.QoS == 1)||(T1.QoS == 2)||(T1.QoS == 3))&&((T2.QoS == 1)&&(T2.QoS == 2)||(T2.QoS == 3)))){
      Local_T = (T1.Temperature + T2.Temperature) / 2;

    } else if(((T1.QoS == 1)||(T1.QoS == 2)||(T1.QoS == 3))&&((T2.QoS != 1)&&(T2.QoS != 2)&&(T2.QoS != 3))){
        Local_T = T1.Temperature;

    } else if(((T1.QoS != 1)&&(T1.QoS != 2)&&(T1.QoS != 3))&&((T2.QoS != 1)||(T2.QoS != 2)||(T2.QoS != 3))){
        Local_T = T2.Temperature;

    } else {
        Local_T = 0.0;

    }

    if(((T1.QoS == 1)||(T1.QoS == 2)||(T1.QoS == 3))||((T2.QoS == 1)||(T2.QoS == 2)||(T2.QoS == 3))){
        
        if(Local_T > BufferTankTempHighLimit){
          PVHeatingOutput = 0;
          RedLightOutput = 0;

        } else if(Local_T < BufferTankTempLowLimit){
          PVHeatingOutput = 1;
          RedLightOutput = 1;

        } else {
          /*If the temperature is between low and max limit, then no heating shall be provided*/
          PVHeatingOutput = 0;
          RedLightOutput = 0;

        }
      
    }  else {
        PVHeatingOutput = 0;
        RedLightOutput = 0;

    }
}


/*The relay and the solid state relay signal shall be delayed as the following:
 * - at switching on the SSR shall be delayed as long as the feedback signal is provided by the PV magnetic switch to
 *   protect the contactor of the magnetic switch
 * - at switch off the magnetic switch switch shall be delayed with 1 second, to also protect its contactors from burning
 *   
*/
void PVHeatingDelays(){

  /*Rising edge detection*/  
  if(PVHeatingOutput && !PVHeatingOutputN1){
    PVHeatingOutputRisingEdge = 1;
    PVHeatingOutputFallingEdge = 0;
    PVHeatingOutputN1 = PVHeatingOutput;
    PVHeatingSSROutputT_ONDelayCounter = 0;
    PVHeatingRelayOutput = 1;
/*Serial.print("PVHeating Rising edge, ");
Serial.print(" PVHeatingRelayOutput: ");
Serial.print(PVHeatingRelayOutput);  
Serial.print(" PVHeatingSSROutput: ");
Serial.println(PVHeatingSSROutput); */
  }

  /*Fallng edge detection*/  
  if(!PVHeatingOutput && PVHeatingOutputN1){
    PVHeatingOutputRisingEdge = 0;
    PVHeatingOutputFallingEdge = 1;
    PVHeatingOutputN1 = PVHeatingOutput;
    PVHeatingRelayOutputT_OFFDelayCounter = 0;
    PVHeatingSSROutput = 0;
/*Serial.print("PVHeating Falling edge, ");
Serial.print(" PVHeatingRelayOutput: ");
Serial.print(PVHeatingRelayOutput);  
Serial.print(" PVHeatingSSROutput: ");
Serial.println(PVHeatingSSROutput);*/   
  }

  if(previousSec != actSecond){
    
    if(PVHeatingOutputRisingEdge){
      PVHeatingSSROutputT_ONDelayCounter++;

      if(PVHeatingSSROutputT_ONDelayCounter > PVHeatingSSROutputT_ONDelay){
        PVHeatingSSROutput = 1;
        PVHeatingOutputRisingEdge = 0;
/*Serial.print(" PVHeatingRelayOutput: ");
Serial.print(PVHeatingRelayOutput);          
Serial.print(" PVHeatingSSROutput: ");
Serial.println(PVHeatingSSROutput);*/

      }
    }

    if(PVHeatingOutputFallingEdge){
      PVHeatingRelayOutputT_OFFDelayCounter++;

      if(PVHeatingRelayOutputT_OFFDelayCounter > PVHeatingRelayOutputT_OFFDelay){
        PVHeatingRelayOutput = 0;
        PVHeatingOutputFallingEdge = 0;
/*Serial.print(" PVHeatingRelayOutput: ");
Serial.print(PVHeatingRelayOutput);          
Serial.print(" PVHeatingSSROutput: ");
Serial.println(PVHeatingSSROutput);*/

      }
    }    

  }


}


/* The pump starting shall only be available if the V1 has reached any of its end positions*/
void PumpStartingCheck(){

    if((!HeatSourceBoilerInput && HeatSourceBufferInput)||(HeatSourceBoilerInput && !HeatSourceBufferInput)){
        PumpCommand = 1;
        ValveTimeOut = 0;
        YellowLightOutput = 1;

    } else {
        if(previousSec != actSecond){
          ValveTimeOut++;
        }

        if(ValveTimeOut > ValveTimeOutLimit){
          PumpCommand = 0;
          YellowLightOutput = 0;

        }

    } 
}



/****** Signal availability check and selection ******/
void SignalAvailabilityCheckAndSelection(){
  TemperatureSignalAvailabilityCheck(&T1);
/*Serial.print("T1.QoS: ");
Serial.println(T1.QoS);*/
  TemperatureSignalAvailabilityCheck(&T2);
/*  TemperatureSignalAvailabilityCheck(&T3);
  TemperatureSignalAvailabilityCheck(&T4);*/
  FlowSignalAvailabilityCheck(&F3);
  FlowSignalAvailabilityCheck(&F4);

}

void TemperatureSignalAvailabilityCheck(struct Temp *T){
  int year_no_diff = 0, month_no_diff = 0, day_no_diff = 0, hour_no_diff = 0, minute_no_diff = 0, second_no_diff = 0;
  /*First, the signals QoS and the msg counters shall be checked
   * - If both are okay, then the timestamp shall be checked and the newest shall be used
   * - If one of them is okay, then use the signal with the okay QoS
   * - If none is okay, then set an error
   
   * All the data in the timestamp shall be checked for diff, if there is no diff use the average.*/
  if((T->CAN_QoS == 1) && (T->RS485_QoS == 1)){
    T->QoS = 1;
      /*Compare the timestamps, starting with the year*/
      int year_diff = T->RS485_TimeStamp.Year() - T->CAN_TimeStamp.Year();

      if(year_diff == 0){
          year_no_diff = 1;
 // Serial.print("No difference in year, ");

      } else if(year_diff < 0){
        T->Temperature = T->CAN_Temperature;
//  Serial.print("CAN is actual(year), ");
      } else {
        T->Temperature = T->RS485_Temperature;
// Serial.print("RS485 is actual(year), ");

      }

  
    /*If there is no difference in year, then check the month*/
    if(year_no_diff){
      int month_diff = T->RS485_TimeStamp.Month() - T->CAN_TimeStamp.Month(); 

      if(month_diff == 0){
          month_no_diff = 1;
  //Serial.print(", No difference in month, ");

      } else if(month_diff < 0){
        T->Temperature = T->CAN_Temperature;
  //Serial.print("CAN is actual(month), ");
      } else {
        T->Temperature = T->RS485_Temperature;
  //Serial.print("RS485 is actual(month), ");

      }
    }

    /*If there is no difference in year and month, then check the day*/
    if(year_no_diff && month_no_diff){
      int day_diff = T->RS485_TimeStamp.Day() - T->CAN_TimeStamp.Day(); 

      if(day_diff == 0){
          day_no_diff = 1;
 // Serial.print(", No difference in day");

      } else if(day_diff < 0){
        T->Temperature = T->CAN_Temperature;
 // Serial.print("CAN is actual(day), ");
      } else {
        T->Temperature = T->RS485_Temperature;
 // Serial.print("RS485 is actual(day),");

      }

    }


    /*If there is no difference in year, month and day, then check the hour*/
    if(year_no_diff && month_no_diff && day_no_diff){
      int hour_diff = T->RS485_TimeStamp.Hour() - T->CAN_TimeStamp.Hour(); 

      if(hour_diff == 0){
          hour_no_diff = 1;
 // Serial.print(", No difference in hour");

      } else if(hour_diff < 0){
        T->Temperature = T->CAN_Temperature;
 // Serial.print("CAN is actual(hour), ");
      } else {
        T->Temperature = T->RS485_Temperature;
 // Serial.print("RS485 is actual(hour), ");

      }
    }

        /*If there is no difference in year, month and hour, then check the minute*/
    if(year_no_diff && month_no_diff && day_no_diff && hour_no_diff){
      int minute_diff = T->RS485_TimeStamp.Minute() - T->CAN_TimeStamp.Minute(); 

      if(minute_diff == 0){
          minute_no_diff = 1;
 // Serial.print(", No difference in minute");

      } else if(minute_diff < 0){
        T->Temperature = T->CAN_Temperature;
 // Serial.print("CAN is actual(minute), ");
      } else {
        T->Temperature = T->RS485_Temperature;
  //Serial.print("RS485 is actual(minute), ");

      }
    }

    /*If there is no difference in year, month and hour, minute then check the second*/
    if(year_no_diff && month_no_diff && day_no_diff && hour_no_diff && minute_no_diff){
      int second_diff = T->RS485_TimeStamp.Second() - T->CAN_TimeStamp.Second(); 

      if(second_diff == 0){
          second_no_diff = 1;
  //Serial.print(", No difference in second");

      } else if(second_diff < 0){
        T->Temperature = T->CAN_Temperature;
  //Serial.print("CAN is actual(second), ");
      } else {
        T->Temperature =T->RS485_Temperature;
  //Serial.print("RS485 is actual(second), ");

      }
    }

    /* If there is no difference in the two timestamp, then consider bothsignals okay from availability and integrity point
     * maybe one of them can be used, but currently the average shall be used.*/
    if(year_no_diff && month_no_diff && day_no_diff && hour_no_diff && minute_no_diff && second_no_diff){
      T->Temperature = (T->RS485_Temperature + T->CAN_Temperature) / 2;

    }

  /* If the CAN is not okay, then signals from RS485 shall be copied*/
  /*2 - CAN Error, 3 - RS485 Error */
  } else if((T->CAN_QoS != 1) && (T->RS485_QoS == 1)){
//Serial.print("T QoS is bad, RS485 selected");
    T->Temperature =T->RS485_Temperature;
    T->QoS = 2;

  /* If the RS485 is not okay, then signals from CAN shall be copied*/
  } else if((T->CAN_QoS == 1) && (T->RS485_QoS != 1)){
//Serial.print("T1 QoS is bad, CAN selected");
    T->Temperature =T->CAN_Temperature;
    T->QoS = 3;

  /*If no QoS is okay, then set the QoS to */  
  } else if((T->CAN_QoS != 1) && (T->RS485_QoS != 1)){
//Serial.print("T1 both QoS is bad, default value");
    T->Temperature = 40000.0;
    T->QoS = 0;
  }
//Serial.println();
}


void FlowSignalAvailabilityCheck(struct Flw *F){
  int year_no_diff = 0, month_no_diff = 0, day_no_diff = 0, hour_no_diff = 0, minute_no_diff = 0, second_no_diff = 0;
  /*First, the signals QoS and the msg counters shall be checked
   * - If both are okay, then the timestamp shall be checked and the newest shall be used
   * - If one of them is okay, then use the signal with the okay QoS
   * - If none is okay, then set an error
   
   * All the data in the timestamp shall be checked for diff, if there is no diff use the average.*/
  if((F->CAN_QoS == 1) && (F->RS485_QoS == 1)){
    F->QoS = 1;
      /*Compare the timestamps, starting with the year*/
      int year_diff = F->RS485_TimeStamp.Year() - F->CAN_TimeStamp.Year();

      if(year_diff == 0){
          year_no_diff = 1;
//  Serial.print("No difference in year, ");

      } else if(year_diff < 0){
        F->Flow = F->CAN_Flow;
//  Serial.print("CAN is actual(year), ");
      } else {
        F->Flow = F->RS485_Flow;
//  Serial.print("RS485 is actual(year), ");

      }

  
    /*If there is no difference in year, then check the month*/
    if(year_no_diff){
      int month_diff = F->RS485_TimeStamp.Month() - F->CAN_TimeStamp.Month(); 

      if(month_diff == 0){
          month_no_diff = 1;
 // Serial.print(", No difference in month, ");

      } else if(month_diff < 0){
        F->Flow = F->CAN_Flow;
//  Serial.print("CAN is actual(month), ");
      } else {
        F->Flow = F->RS485_Flow;
//  Serial.print("RS485 is actual(month), ");

      }
    }

    /*If there is no difference in year and month, then check the day*/
    if(year_no_diff && month_no_diff){
      int day_diff = F->RS485_TimeStamp.Day() - F->CAN_TimeStamp.Day(); 

      if(day_diff == 0){
          day_no_diff = 1;
//  Serial.print(", No difference in day");

      } else if(day_diff < 0){
        F->Flow = F->CAN_Flow;
//  Serial.print("CAN is actual(day), ");
      } else {
        F->Flow = F->RS485_Flow;
//  Serial.print("RS485 is actual(day),");

      }

    }


    /*If there is no difference in year, month and day, then check the hour*/
    if(year_no_diff && month_no_diff && day_no_diff){
      int hour_diff = F->RS485_TimeStamp.Hour() - F->CAN_TimeStamp.Hour(); 

      if(hour_diff == 0){
          hour_no_diff = 1;
//  Serial.print(", No difference in hour");

      } else if(hour_diff < 0){
        F->Flow = F->CAN_Flow;
//  Serial.print("CAN is actual(hour), ");
      } else {
        F->Flow = F->RS485_Flow;
//  Serial.print("RS485 is actual(hour), ");

      }
    }

        /*If there is no difference in year, month and hour, then check the minute*/
    if(year_no_diff && month_no_diff && day_no_diff && hour_no_diff){
      int minute_diff = F->RS485_TimeStamp.Minute() - F->CAN_TimeStamp.Minute(); 

      if(minute_diff == 0){
          minute_no_diff = 1;
//  Serial.print(", No difference in minute");

      } else if(minute_diff < 0){
        F->Flow = F->CAN_Flow;
//  Serial.print("CAN is actual(minute), ");
      } else {
        F->Flow = F->RS485_Flow;
//  Serial.print("RS485 is actual(minute), ");

      }
    }

    /*If there is no difference in year, month and hour, minute then check the second*/
    if(year_no_diff && month_no_diff && day_no_diff && hour_no_diff && minute_no_diff){
      int second_diff = F->RS485_TimeStamp.Second() - F->CAN_TimeStamp.Second(); 

      if(second_diff == 0){
          second_no_diff = 1;
//  Serial.print(", No difference in second");

      } else if(second_diff < 0){
        F->Flow = F->CAN_Flow;
//  Serial.print("CAN is actual(second), ");
      } else {
        F->Flow = F->RS485_Flow;
//  Serial.print("RS485 is actual(second), ");

      }
    }

    /* If there is no difference in the two timestamp, then consider bothsignals okay from availability and integrity point
     * maybe one of them can be used, but currently the average shall be used.*/
    if(year_no_diff && month_no_diff && day_no_diff && hour_no_diff && minute_no_diff && second_no_diff){
      F->Flow = (F->RS485_Flow + F->CAN_Flow) / 2;

    }

  /* If the CAN is not okay, then signals from RS485 shall be copied*/
  /*2 - CAN Error, 3 - RS485 Error*/
  } else if((F->CAN_QoS != 1) && (F->RS485_QoS == 1)){
//Serial.print("T QoS is bad, RS485 selected");
    F->Flow = F->RS485_Flow;
    F->QoS = 2;

  /* If the RS485 is not okay, then signals from CAN shall be copied*/
  } else if((F->CAN_QoS == 1) && (F->RS485_QoS != 1)){
//Serial.print("T1 QoS is bad, CAN selected");
    F->Flow = F->CAN_Flow;
    F->QoS = 3;

  /*If no QoS is okay, then set the QoS to */  
  } else if((F->CAN_QoS != 1) && (F->RS485_QoS != 1)){
//Serial.print("T1 both QoS is bad, default value");
    F->Flow = 40000.0;
    F->QoS = 0;
  }
//  Serial.println();
}
/****** end of Signal availability check and selection ******/





void RS485MsgReceiving(){
    if (Serial3.available()) {
        char inputByte = Serial3.read();

        // ha van hely, eltároljuk
        if (dataIndex < BUFFER_SIZE - 1) {
            DataToBeProcessed[dataIndex++] = inputByte;
            DataToBeProcessed[dataIndex] = '\0';  // mindig lezárjuk
        }

        // ha newline jött, akkor feldolgozás
        if (inputByte == '\n') {
            char localBuffer[BUFFER_SIZE];
//Serial.print("Serial data: ");
//Serial.println(DataToBeProcessed);*/
            strncpy(localBuffer, DataToBeProcessed, BUFFER_SIZE);

            // reseteljük a globális buffert
            dataIndex = 0;
            DataToBeProcessed[0] = '\0';

            // --- CRC leválasztása ---
            char *lastComma = strrchr(localBuffer, ',');
            if (!lastComma) return; // nincs CRC mező
            int CRC_int = atoi(lastComma + 1);
//Serial.print("CRC: ");
//Serial.print(CRC_int);
            *lastComma = '\0'; // levágjuk a CRC-t

// --- CRC újraszámítás ---
            uint8_t DataInByte[14] = {0};
//Serial.print(", localBuffer: ");
//Serial.print(localBuffer);
            strncpy((char*)DataInByte, localBuffer, sizeof(DataInByte));
            int crc_calculated = calcCRC8(DataInByte, sizeof(DataInByte));
//Serial.print(", Calculated CRC: ");
//Serial.print(crc_calculated);

            // --- MsgCounter leválasztása ---
            char *msgCounterComma = strrchr(localBuffer, ',');
            if (!msgCounterComma) return;
            int MsgCounter = atoi(msgCounterComma + 1);
//Serial.print(", Msg counter: ");
//Serial.print(MsgCounter);            
            *msgCounterComma = '\0';

            // --- ID és érték leválasztása ---
            char *colonPos = strrchr(localBuffer, ':');
            if (!colonPos) return;
            float RecvData = atof(colonPos + 1);
            *colonPos = '\0';
            char RecvDataID[8];
            strncpy(RecvDataID, localBuffer, sizeof(RecvDataID));
//Serial.print(", Rec data ID: ");
//Serial.print(RecvDataID);
            RecvDataID[sizeof(RecvDataID)-1] = '\0';
            
            // --- CRC validálás ---
            if (crc_calculated != CRC_int) return;
//Serial.print("CRC OK");
            // --- ID alapján eltárolás ---
            if (strcmp(RecvDataID, "T1") == 0) {
                if ((MsgCounter != T1.RS485_PrevMsgCounter) && (RecvData > TemperaturePlausibilityMin) && (RecvData < TemperaturePlausibilityMax) && ((abs(RecvData - T1N.RS485_Temperature) > TemperatureChangeLimit))) {
//Serial.print("Msg counter OK");                  
                    T1.RS485_timeout = 0;
                    T1.RS485_Temperature = RecvData;
                    T1.RS485_QoS = 1;
                    T1.RS485_TimeStamp = Rtc.GetDateTime();
                    T1.RS485_PrevMsgCounter = MsgCounter;
//                    Serial.print("RS485 T1 received: ");
//                    Serial.println(RecvData);
                } else {
                    T1.RS485_timeout = 0;
                    T1.RS485_Temperature = 40000.0;
                    T1.RS485_QoS = 0;
                    T1.RS485_TimeStamp = Default_TimeStamp;
                    T1.RS485_PrevMsgCounter = 255;
                    T1.RS485_TimeStamp = Default_TimeStamp;
                }
            } 
            else if (strcmp(RecvDataID, "T2") == 0) {
                if ((MsgCounter != T2.RS485_PrevMsgCounter) && (RecvData > TemperaturePlausibilityMin) && (RecvData < TemperaturePlausibilityMax) && ((abs(RecvData - T2N.RS485_Temperature) > TemperatureChangeLimit))){
                    T2.RS485_timeout = 0;
                    T2.RS485_Temperature = RecvData;
                    T2.RS485_QoS = 1;
                    T2.RS485_TimeStamp = Rtc.GetDateTime();
                    T2.RS485_PrevMsgCounter = MsgCounter;
//                    Serial.print("RS485 T1 received: ");
//                    Serial.println(RecvData);
                } else {
                    T2.RS485_timeout = 0;
                    T2.RS485_Temperature = 40000.0;
                    T2.RS485_QoS = 0;
                    T2.RS485_TimeStamp = Default_TimeStamp;
                    T2.RS485_PrevMsgCounter = 255;
                    T2.RS485_TimeStamp = Default_TimeStamp;
                }
            }
            else if (strcmp(RecvDataID, "T3") == 0) {
                // ugyanaz mint T1, csak T3-ra
            }
            else if (strcmp(RecvDataID, "T4") == 0) {
                // ugyanaz mint T1, csak T4-re
            }
            else if (strcmp(RecvDataID, "F3") == 0) {
                if (MsgCounter != F3.RS485_PrevMsgCounter) {
                    F3.RS485_timeout = 0;
                    F3.RS485_Flow = RecvData;
                    F3.RS485_QoS = 1;
                    F3.RS485_TimeStamp = Rtc.GetDateTime();
                    F3.RS485_PrevMsgCounter = MsgCounter;
//                    Serial.print("RS485 F3 received: ");
//                    Serial.println(RecvData);
                } else {
                    F3.RS485_timeout = 0;
                    F3.RS485_Flow = 40000.0;
                    F3.RS485_QoS = 0;
                    F3.RS485_TimeStamp = Default_TimeStamp;
                    F3.RS485_PrevMsgCounter = 255;
                    F3.RS485_TimeStamp = Default_TimeStamp;
                }
            }
            else if (strcmp(RecvDataID, "F4") == 0) {
                if (MsgCounter != F4.RS485_PrevMsgCounter) {
                    F4.RS485_timeout = 0;
                    F4.RS485_Flow = RecvData;
                    F4.RS485_QoS = 1;
                    F4.RS485_TimeStamp = Rtc.GetDateTime();
                    F4.RS485_PrevMsgCounter = MsgCounter;
//                    Serial.print("RS485 F4 received: ");
//                    Serial.println(RecvData);
                } else {
                    F4.RS485_timeout = 0;
                    F4.RS485_Flow = 40000.0;
                    F4.RS485_QoS = 0;
                    F4.RS485_TimeStamp = Default_TimeStamp;
                    F4.RS485_PrevMsgCounter = 255;
                    F4.RS485_TimeStamp = Default_TimeStamp;
                }
            }
        }
//        Serial.println();
    }
}



void CANMsgReceiving(){
  if(mcp2515.readMessage(&RecvcanMsg) == MCP2515::ERROR_OK){
//Serial.println("CAN Msg received");
    /* If a message was received, then check the CRC*/
      /* getting the received CRC value */
      byte CRC = RecvcanMsg.data[5];
      byte MsgCounter =  RecvcanMsg.data[4];
      /* calculating the CRC again*/
      CRCcalc[0] = RecvcanMsg.data[0];
      CRCcalc[1] = RecvcanMsg.data[1];
      CRCcalc[2] = RecvcanMsg.data[2];
      CRCcalc[3] = RecvcanMsg.data[3];
      CRCcalc[4] = RecvcanMsg.data[4];
      byte crc_calculated = calcCRC8(CRCcalc, 5);


    /* T1 msg*/
      if(RecvcanMsg.can_id == 0x020){     
       
        /* If T1 was received then first, set the timeout to 0*/
        T1.CAN_timeout = 0;  
       
        float T1temp = 0.0;
        /* first getting the value */
        ((uint8_t*)&T1temp)[0] = RecvcanMsg.data[0];
        ((uint8_t*)&T1temp)[1] = RecvcanMsg.data[1];
        ((uint8_t*)&T1temp)[2] = RecvcanMsg.data[2];
        ((uint8_t*)&T1temp)[3] = RecvcanMsg.data[3];

        /*comparing the received and calculated CRC and modifying the QoS
         * based on the comparization*/
        if((crc_calculated == CRC) && (T1.CAN_PrevMsgCounter != MsgCounter) && (T1temp > TemperaturePlausibilityMin) && (T1temp < TemperaturePlausibilityMax) && ((abs(T1temp - T1N.CAN_Temperature) > TemperatureChangeLimit))){
        
          T1.CAN_QoS = 1;
          T1.CAN_Temperature = T1temp;
          T1.CAN_TimeStamp = Rtc.GetDateTime();
          T1.CAN_PrevMsgCounter = MsgCounter;

/*Serial.print("CAN T1 received:"); 
Serial.println(T1temp);
Serial.print("CAN: T1 received, temp:");
Serial.print(T1.CAN_Temperature);
Serial.print(", timestamp: Year ");
Serial.print(T1.CAN_TimeStamp.Year());
Serial.print(", Month: ");
Serial.print(T1.CAN_TimeStamp.Month());
Serial.print(", Day: ");
Serial.print(T1.CAN_TimeStamp.Day());
Serial.print(", Hour: ");
Serial.print(T1.CAN_TimeStamp.Hour());
Serial.print(", Minute: ");
Serial.print(T1.CAN_TimeStamp.Minute());
Serial.print(", Second: ");
Serial.println(T1.CAN_TimeStamp.Second());*/

        } else {
          T1.CAN_QoS = 0;
          T1.CAN_Temperature = 999.9;
        }


    /* T2 msg*/
      } else if (RecvcanMsg.can_id == 0x030){        
        
        /* If T2 was received then first, set the timeout to 0*/
        T2.CAN_timeout = 0;
...

This file has been truncated, please download it to see its full contents.

T1_TemperatureMeasurement

C/C++
#include <SPI.h>
#include <mcp2515.h>
#include "max6675.h"
#include <avr/wdt.h>
#include "CRC.h"
#include <Arduino.h>
#include <TM1637Display.h>

// Module connection pins (Digital Pins)
#define CLK 7
#define DIO 8

// RS485 connection pins
#define DE 3
#define RE 2

TM1637Display display(CLK, DIO);

struct can_frame canMsg1;

MCP2515 mcp2515(10);

int thermoDO1 = 4;
int thermoCS1 = 5;
int thermoCLK1 = 6;

int state_variable = 0;

int TestButtonPin = 9, TestButton = 0;

float temperature = 0.0;

String ToBeSent = "T1:";
byte StringInChar[14] = "";
byte MsgCount = 0;

MAX6675 thermocouple1(thermoCLK1, thermoCS1, thermoDO1);

byte CRCCalc[5];

uint8_t Displaydata[4] = { 0xff, 0xff, 0xff, 0xff };

const uint8_t SEG_T1[] = {
	SEG_D,                                            // _
	SEG_D | SEG_E | SEG_F | SEG_G,                    // t
	SEG_G,                                            // -
	SEG_B | SEG_C                                     // 1
	}; 


void setup() {

  Serial.begin(115200);
  Serial.println("T1 Startup");

  wdt_disable();/* diable the watchdog and wait for more than 2 seconds */
  delay(3000); /* Done so the Arduino doesnot keep resetting infinitely in case of wrong configuration*/
  wdt_enable(WDTO_4S); /* Enable the watchdog with a timeout of 2 seconds */

  canMsg1.can_id  = 0x020;
  canMsg1.can_dlc = 8;
  canMsg1.data[0] = 0x00;
  canMsg1.data[1] = 0x00;
  canMsg1.data[2] = 0x00;
  canMsg1.data[3] = 0x00;
  canMsg1.data[4] = 0x00;
  canMsg1.data[5] = 0x00;
  canMsg1.data[6] = 0x00;
  canMsg1.data[7] = 0x00;

  mcp2515.reset();
  mcp2515.setBitrate(CAN_500KBPS);
  mcp2515.setNormalMode();

  //pinMode(13, OUTPUT);

  // Set the DE and RE pins as outputs
  pinMode(DE, OUTPUT);
  pinMode(RE, OUTPUT);
  // Set DE and RE high to enable transmission mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  pinMode(TestButtonPin, INPUT_PULLUP);

  display.setBrightness(0x0f);

  // wait for MAX chip to stabilize
  delay(550);
}

void loop() {
  TestButton = digitalRead(TestButtonPin);
/*Serial.print("Button State");
Serial.println(TestButton);*/

  MsgCount++;
  temperature = thermocouple1.readCelsius();

if(!TestButton){
  temperature = 73.0;
}
  ToBeSent = "T1:";
  ToBeSent+=String(temperature,2);
  ToBeSent+=",";
  ToBeSent+=String(MsgCount);  
  ToBeSent.toCharArray(StringInChar, 14);
  byte RS485crc_calculated = calcCRC8(StringInChar, 14);
  ToBeSent+=",";
  ToBeSent+=String(RS485crc_calculated);
  ToBeSent+="\n";
  
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH); 
  Serial.print(ToBeSent);
  Serial.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  CRCCalc[0] = ((uint8_t*)&temperature)[0];
  CRCCalc[1] = ((uint8_t*)&temperature)[1];
  CRCCalc[2] = ((uint8_t*)&temperature)[2];
  CRCCalc[3] = ((uint8_t*)&temperature)[3];
  CRCCalc[4] = MsgCount;

  byte crc_calculated = calcCRC8(CRCCalc, 5);

  canMsg1.data[0] = ((uint8_t*)&temperature)[0];
  canMsg1.data[1] = ((uint8_t*)&temperature)[1];
  canMsg1.data[2] = ((uint8_t*)&temperature)[2];
  canMsg1.data[3] = ((uint8_t*)&temperature)[3];
  canMsg1.data[4] = MsgCount;
  canMsg1.data[5] = crc_calculated;

  mcp2515.sendMessage(&canMsg1); 

  if(state_variable){
    //digitalWrite(13, state_variable);
    state_variable = 0;
    display.clear();
    display.showNumberDec((int)temperature, false);

  } else {
    //digitalWrite(13, state_variable);
    state_variable = 1;
    display.clear();
    display.setSegments(SEG_T1);

  }
  
   wdt_reset(); /* Reset the wdt timer */
   unsigned long DelayTime = random(1800, 2200);
   delay(DelayTime);

   /*Reset the uC after 255 execution to try to stabilize the communication*/
  if(MsgCount >= 254){
    while(1){}
  }
}

T2_TemperatureMeasurement

C/C++
#include <SPI.h>
#include <mcp2515.h>
#include "max6675.h"
#include <avr/wdt.h>
#include "CRC.h"
#include <Arduino.h>
#include <TM1637Display.h>

// Module connection pins (Digital Pins)
#define CLK 7
#define DIO 8

// RS485 connection pins
#define DE 3
#define RE 2

TM1637Display display(CLK, DIO);

struct can_frame canMsg1;
struct can_frame RecvcanMsg;

MCP2515 mcp2515(10);

int thermoDO1 = 4;
int thermoCS1 = 5;
int thermoCLK1 = 6;

int state_variable = 0;

int TestButtonPin = 9, TestButton = 0;

float temperature = 0.0;

String ToBeSent = "T2:";
byte StringInChar[14] = "";
byte MsgCount = 0;

MAX6675 thermocouple1(thermoCLK1, thermoCS1, thermoDO1);

byte CRCCalc[5];

uint8_t Displaydata[4] = { 0xff, 0xff, 0xff, 0xff };

const uint8_t SEG_T2[] = {
	SEG_D,                                            // _
	SEG_D | SEG_E | SEG_F | SEG_G,                    // t
	SEG_G,                                            // -
	SEG_A | SEG_B | SEG_D | SEG_E | SEG_G             // 2
	}; 

void setup() {

  Serial.begin(115200);
  Serial.println("T2 Startup");

  wdt_disable();/* diable the watchdog and wait for more than 2 seconds */
  delay(3000); /* Done so the Arduino doesnot keep resetting infinitely in case of wrong configuration*/
  wdt_enable(WDTO_4S); /* Enable the watchdog with a timeout of 2 seconds */

  canMsg1.can_id  = 0x030;
  canMsg1.can_dlc = 8;
  canMsg1.data[0] = 0x00;
  canMsg1.data[1] = 0x00;
  canMsg1.data[2] = 0x00;
  canMsg1.data[3] = 0x00;
  canMsg1.data[4] = 0x00;
  canMsg1.data[5] = 0x00;
  canMsg1.data[6] = 0x00;
  canMsg1.data[7] = 0x00;

  mcp2515.reset();
  mcp2515.setBitrate(CAN_500KBPS);
  mcp2515.setNormalMode();

  pinMode(13, OUTPUT);

  // Set the DE and RE pins as outputs
  pinMode(DE, OUTPUT);
  pinMode(RE, OUTPUT);
  // Set DE and RE high to enable transmission mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  pinMode(TestButtonPin, INPUT_PULLUP);

  display.setBrightness(0x0f);

  // wait for MAX chip to stabilize
  delay(750);
}

void loop() {
  TestButton = digitalRead(TestButtonPin);
/*Serial.print("Button State");
Serial.println(TestButton);*/

  MsgCount++;
  temperature = thermocouple1.readCelsius();

  if(!TestButton){
    temperature = 73.0;
  }

  ToBeSent = "T2:";
  ToBeSent+=String(temperature,2);
  ToBeSent+=",";
  ToBeSent+=String(MsgCount);
  ToBeSent.toCharArray(StringInChar, 14);
  byte RS485crc_calculated = calcCRC8(StringInChar, 14);
  ToBeSent+=",";
  ToBeSent+=String(RS485crc_calculated);
  ToBeSent+="\n";
  
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH); 
  Serial.print(ToBeSent);
  Serial.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  CRCCalc[0] = ((uint8_t*)&temperature)[0];
  CRCCalc[1] = ((uint8_t*)&temperature)[1];
  CRCCalc[2] = ((uint8_t*)&temperature)[2];
  CRCCalc[3] = ((uint8_t*)&temperature)[3];
  CRCCalc[4] = MsgCount;

  byte crc_calculated = calcCRC8(CRCCalc, 5);

  canMsg1.data[0] = ((uint8_t*)&temperature)[0];
  canMsg1.data[1] = ((uint8_t*)&temperature)[1];
  canMsg1.data[2] = ((uint8_t*)&temperature)[2];
  canMsg1.data[3] = ((uint8_t*)&temperature)[3];
  canMsg1.data[4] = MsgCount;
  canMsg1.data[5] = crc_calculated;

  mcp2515.sendMessage(&canMsg1);

  if(state_variable){
    digitalWrite(13, state_variable);
    state_variable = 0;
    display.clear();
    display.showNumberDec((int)temperature, false);

  } else {
    digitalWrite(13, state_variable);
    state_variable = 1;
    display.clear();
    display.setSegments(SEG_T2);

  }
   wdt_reset(); /* Reset the wdt timer */
   unsigned long DelayTime = random(1800, 2200);
   delay(DelayTime);

  /*Reset the uC after 255 execution to try to stabilize the communication*/
  if(MsgCount >= 254){
    while(1){}
  }
}

Credits

Viktor Kurusa
1 project • 11 followers

Comments