#include <Arduino.h>
#include <EasyNextionLibrary.h>
#include <neotimer.h>
#include <EEPROM.h>
#include <time.h>
EasyNex myNex(Serial1);
const uint8_t firmwareVersion[] = __DATE__ " " __TIME__;
//--------------- VARIABLES ----------------
//------------------------------------------
const int fan = 61; // This same pin would be used for controling a damper
const int rampInterval = 5;
const int rampSpeed = 50;
const int fadeInterval = 2;
const float targetAirFlow = 0.5;
volatile int fanSpeed = 0;
volatile int airFlow1 = 0;
const int lamFan = 60; // This is for the laminar air fan
const int lamRampInterval = 2;
const int lamRampSpeed = 30;
const int lamFadeInterval = 1;
const int lamTargetAirFlow = 0.3;
volatile int lamFanSpeed = 0;
volatile int airFlow2 = 0;
volatile bool messageSent = 0;
const int numReadings = 10; // Number of readings to take to take an average
volatile int airFlow1Readings[numReadings];
volatile int airFlow1ReadingIndex = 0;
volatile int airFlow1Total = 0;
volatile int airFlow1Average = 0;
volatile int airFlow2Readings[numReadings];
volatile int airFlow2ReadingIndex = 0;
volatile int airFlow2Total = 0;
volatile int airFlow2Average = 0;
volatile int autoSashEn = 0; // 0 => Autosash Disabled 1 => Autosash Enabled
volatile bool emrStop = 0;
volatile bool sashUpFlag = 0;
volatile bool sashDownFlag = 0;
volatile int sashTime = 5000; // No motion time for the neotimer
char ON = "HIGH";
char OFF = "LOW";
volatile int sashLevel = 0; // 0 => No reading 1 => Sash High Level 2 => Sash Mid Level 3 => Sash Low Level 4 => Sash Positioning Error
//----------------- TIMERS -----------------
//------------------------------------------
Neotimer noMotion = Neotimer(sashTime);
//----------------- EEPROM -----------------
//------------------------------------------
uint8_t FIRMWARE_VER = 1; //Assign EEPROM addresses
uint8_t RAMP_INTERVAL = 2;
uint8_t RAMP_SPEED = 3;
uint8_t FADE_INTERVAL = 4;
uint8_t TARGET_AIRFLOW = 5;
uint8_t NUM_READINGS = 6;
uint8_t AUTO_SASH_EN = 7;
uint8_t SASH_UP_FLAG = 10;
uint8_t SASH_DOWN_FLAG = 11;
uint8_t EMR_STOP = 12;
uint8_t SASH_TIME = 13;
//------------ PHYSICAL PINOUTS ------------
//------------------------------------------
const int MCU1 = 10; // => FAN POWER REALY - 240V
const int MCU2 = 11; // => INTERNAL LED LIGHT RELAY - 240V
const int MCU3 = 12; // => FASCIA SOCKETS RELAY - 240V
const int MCU4 = 13; // => INTERNAL RED LIGHT RELAY - 12V (THIS NEEDS REMAPPING TO THE CORRECT PIN NUMBER. IT IS PH7(T4) ON THE IC.)
const int MCU5 = 22; // =>
const int MCU6 = 23; // =>
const int MCU7 = 24; // => CLUTCH RELAY - 24V
const int MCU8 = 25; // => SASH UP RELAY - 240V
const int MCU9 = 26; // => SASH DOWN RELAY - 240V
const int DIN1 = 78; // =>
const int DIN2 = 79; // =>
const int DIN3 = 80; // => MOTION DETECTOR - 24V
const int DIN4 = 81; // =>
const int DIN5 = A8; // => SASH HIGH - 24V
const int DIN6 = A9; // => SASH MID - 24V
const int DIN7 = A10; // => SASH LOW - 24V
const int DIN8 = A11; // => E-STOP - 24V
const int AIN1 = A0; // => ANALOG IN 1 - FLOW SENSOR - 0-5V
const int AIN2 = A1; // =>
const int AIN3 = A2; // =>
const int AIN4 = A3; // =>
const int AGOT1 = A7; // => ANALOG OUT 1 - FAN SPEED CONTROL - 0-10V
const int AGOT2 = A6; // => ANALOG OUT 2 - AUXILLARY ANALOG OUTPUT - 0-10V
//--------------- FUNCTIONS ----------------
//------------------------------------------
void averageAirFlow1() {
airFlow1Total = airFlow1Total - airFlow1Readings[airFlow1ReadingIndex];
airFlow1Readings[airFlow1ReadingIndex] = analogRead(airFlow1);
airFlow1Total = airFlow1Total + airFlow1Readings[airFlow1ReadingIndex];
airFlow1ReadingIndex = airFlow1ReadingIndex + 1;
if (airFlow1ReadingIndex >= numReadings) // If the array is full, start again
{
airFlow1ReadingIndex = 0;
}
airFlow1Average = airFlow1Total / numReadings; // Calculate the average
}
void averageLaminarAirFlow() {
airFlow2Total = airFlow2Total - airFlow2Readings[airFlow2ReadingIndex];
airFlow2Readings[airFlow2ReadingIndex] = analogRead(airFlow2);
airFlow2Total = airFlow2Total + airFlow2Readings[airFlow2ReadingIndex];
airFlow2ReadingIndex = airFlow2ReadingIndex + 1;
if (airFlow2ReadingIndex >= numReadings)
{
airFlow2ReadingIndex = 0;
}
airFlow2Average = airFlow2Total / numReadings;
}
void adjustAirFlow() {
if(airFlow1Average < targetAirFlow) {
fanSpeed = fanSpeed + rampInterval;
analogWrite(fan, fanSpeed);
if (messageSent == 0)
{
Serial.println("Airflow Too Low, Increasing Fan Speed!");
messageSent = 1;
}
messageSent = 0;
}
else if (airFlow1Average > targetAirFlow)
{
fanSpeed = fanSpeed - fadeInterval;
analogWrite(fan, fanSpeed);
}
}
void adjustLaminarAir() {
if(airFlow2Average < lamTargetAirFlow) {
lamFanSpeed = lamFanSpeed + lamRampInterval;
if(messageSent == 0) {
Serial.println("Laminar Fan Accelerating!");
messageSent = 1;
}
messageSent = 0;
}
else if (airFlow2Average > lamTargetAirFlow) {
lamFanSpeed = lamFanSpeed - lamFadeInterval;
analogWrite(lamFan, lamFanSpeed);
if (messageSent == 0)
{
Serial.println("Laminar Fan Decelerating!");
messageSent = 1;
}
}
messageSent = 0;
}
void setFlags() {
if((digitalRead(DIN6) == HIGH) && (autoSashEn == 1) && (noMotion.done() == 1)) {
sashDownFlag = 1;
sashUpFlag = 0;
} //DIN6 is sash low
else if ((digitalRead(DIN5) == HIGH) && (autoSashEn == 1) && (digitalRead(DIN3) == HIGH)) {
sashDownFlag = 0;
sashUpFlag = 1;
}
else{
sashDownFlag = 0;
sashUpFlag = 0;
}
}
void rSashUp() {
if(digitalRead(DIN5) == HIGH) {
Serial.println("Running Sash Up");
//digitalWrite(MCU9, OFF); // Just in case it has been running
digitalWrite(MCU7, ON);
digitalWrite(MCU8, ON);
}
}
void rSashDown() {
//digitalWrite(MCU8, OFF); // Just in case it has been running
digitalWrite(MCU7, ON);
digitalWrite(MCU9, ON);
}
void sSashUp() {
digitalWrite(MCU7, OFF);
digitalWrite(MCU8, OFF);
}
void sSashDown() {
digitalWrite(MCU7, OFF);
digitalWrite(MCU9, OFF);
}
void runSash() {
if (sashDownFlag == 1) {
rSashDown();
}
else {
sSashDown();
}
if (sashUpFlag == 1) {
rSashUp();
}
else {
sSashUp();
}
}
void killSash() { // Use this to stop sash motion any time.
digitalWrite(MCU8, OFF);
digitalWrite(MCU9, OFF);
digitalWrite(MCU7, OFF);
noMotion.reset();
noMotion.start();
}
void timerReset() {
if(digitalRead(DIN3) == HIGH) {
noMotion.reset();
noMotion.start();
}
}
void setSHFL() {
sashUpFlag = 0; // High level switch has been triggered set sashup flag to 0 to stop movement
killSash();
}
void setSLFL() {
sashDownFlag = 0; // Low level switch has been triggered set sashdown flag to 0 to stop movement
killSash();
}
void sashPosition() {
if (digitalRead(DIN5)) // If sash high is broken only sash is high level
{
sashLevel = 1;
}
else if (digitalRead(DIN5) && digitalRead(DIN5)) // If sash high and mid is broken sash is at operating height
{
sashLevel = 2;
}
else if (digitalRead(DIN6) && digitalRead(DIN7)) // If sash low and mid is broken sash is at low level (closed)
{
sashLevel = 3;
}
else // Sash level error state
{
sashLevel = 4;
}
}
//-------------- HMI COMMANDS --------------
//------------------------------------------
void trigger0() { // This is called when the fan switch is toggled
digitalWrite(MCU1, !digitalRead(MCU1));
if (digitalRead(MCU1) == ON) {
digitalWrite(MCU1, ON);
} else if (digitalRead(MCU1) == OFF) {
digitalWrite(MCU1, OFF);
}
Serial.println("Fan");
}
void trigger1() { // This is called when the light switch is toggled
digitalWrite(MCU2, !digitalRead(MCU2));
if (digitalRead(MCU2) == ON) {
digitalWrite(MCU2, ON);
} else if (digitalRead(MCU2) == OFF) {
digitalWrite(MCU2, OFF);
}
}
void trigger2() { // This is called when the socket switch is toggled
digitalWrite(MCU3, !digitalRead(MCU3));
if (digitalRead(MCU3) == ON) {
digitalWrite(MCU3, ON);
} else if (digitalRead(MCU3) == OFF) {
digitalWrite(MCU3, OFF);
}
}
void trigger3() { // This is called when the auto-sash switch is toggled
if(autoSashEn==0){
autoSashEn = 1;
noMotion.start(); //Enables autosash and starts timer
}
else {
autoSashEn = 0;
digitalWrite(MCU8, OFF); //Stop the sash on the offchance it is still moving
digitalWrite(MCU9, OFF);
digitalWrite(MCU7, OFF);
noMotion.reset(); //Disables the autosash and resets the timer for next use
}
}
void trigger5() { // This is called when the sash up button is pressed
digitalWrite(MCU8, !digitalRead(MCU8));
if (digitalRead(MCU8) == ON) {
rSashUp();
} else if (digitalRead(MCU8) == OFF) {
sSashUp();
}
}
void trigger6() { // This is called when the sash down button is pressed
digitalWrite(MCU9, !digitalRead(MCU9));
if (digitalRead(MCU9) == OFF) {
rSashDown();
} else if (digitalRead(MCU9) == ON) {
sSashDown();
}
}
void trigger7() { // This is called from the "BACKUP" button on the HMI
EEPROM.update(FIRMWARE_VER, firmwareVersion);
EEPROM.update(RAMP_INTERVAL, rampInterval);
EEPROM.update(RAMP_SPEED, rampSpeed);
EEPROM.update(FADE_INTERVAL, fadeInterval);
EEPROM.update(TARGET_AIRFLOW, targetAirFlow);
EEPROM.update(NUM_READINGS, numReadings);
EEPROM.update(AUTO_SASH_EN, autoSashEn);
EEPROM.update(SASH_UP_FLAG, sashUpFlag);
EEPROM.update(SASH_DOWN_FLAG, sashDownFlag);
EEPROM.update(EMR_STOP, emrStop);
EEPROM.update(SASH_TIME, sashTime);
}
//------------- DATA TRANSFER --------------
//------------------------------------------
void wifiDataTransfer() { // Need to use Serial.parseInt() for each value on the ESP32
Serial3.print(sashLevel); // int sashLevel = Serial.parseInt();
Serial3.print(digitalRead(MCU1) + " "); // int MCU1 = Serial.parseInt();
Serial3.print(digitalRead(MCU2) + " ");
Serial3.print(digitalRead(MCU3) + " ");
Serial3.print(digitalRead(MCU4) + " ");
Serial3.print(digitalRead(MCU5) + " ");
Serial3.print(digitalRead(MCU6) + " ");
Serial3.print(digitalRead(MCU7) + " ");
Serial3.print(digitalRead(MCU8) + " ");
Serial3.print(digitalRead(MCU9) + " ");
Serial3.print(digitalRead(DIN1) + " ");
Serial3.print(digitalRead(DIN2) + " ");
Serial3.print(digitalRead(DIN3) + " ");
Serial3.print(digitalRead(DIN4) + " ");
Serial3.print(digitalRead(DIN5) + " ");
Serial3.print(digitalRead(DIN6) + " ");
Serial3.print(digitalRead(DIN7) + " ");
Serial3.print(digitalRead(DIN8) + " ");
Serial3.print(analogRead(AIN1) + " ");
Serial3.print(analogRead(AIN2) + " ");
Serial3.print(analogRead(AIN3) + " ");
Serial3.print(analogRead(AIN4) + " ");
Serial3.print(analogRead(AGOT1) + " ");
Serial3.print(analogRead(AGOT2));
Serial3.println();
}
void setup() {
myNex.begin(115200);
Serial.begin(115200);
pinMode(MCU1, OUTPUT);
pinMode(MCU2, OUTPUT);
pinMode(MCU3, OUTPUT);
pinMode(MCU4, OUTPUT);
pinMode(MCU5, OUTPUT);
pinMode(MCU6, OUTPUT);
pinMode(MCU7, OUTPUT);
pinMode(MCU8, OUTPUT);
pinMode(MCU9, OUTPUT);
pinMode(MCU9, OUTPUT);
pinMode(DIN1, INPUT);
pinMode(DIN2, INPUT);
pinMode(DIN3, INPUT);
pinMode(DIN4, INPUT);
pinMode(DIN5, INPUT);
pinMode(DIN6, INPUT);
pinMode(DIN7, INPUT);
pinMode(DIN8, INPUT);
pinMode(AIN1, INPUT);
pinMode(AIN2, INPUT);
pinMode(AIN3, INPUT);
pinMode(AIN4, INPUT);
pinMode(AGOT1, OUTPUT);
pinMode(AGOT2, OUTPUT);
noMotion.start();
attachInterrupt(digitalPinToInterrupt(DIN3), timerReset, RISING);
attachInterrupt(digitalPinToInterrupt(DIN5), setSHFL, FALLING);
attachInterrupt(digitalPinToInterrupt(DIN6), setSLFL, FALLING);
for (int thisAirFlow1Reading = 0; thisAirFlow1Reading < numReadings; thisAirFlow1Reading++) { // This clears all aspects of the airFlow1Readings array (sets everything to 0)
airFlow1Readings[thisAirFlow1Reading] = 0;
}
}
void loop() {
myNex.NextionListen();
averageAirFlow1();
averageLaminarAirFlow();
adjustAirFlow();
if (autoSashEn == 1)
{
setFlags();
runSash();
}
}