/************************************************************************************/
//
//
// ╭━━━╮╱╱╱╱╱╱╱╱╱╭━━╮╱╱╱╱╱╭╮╱╭╮
// ┃╭━╮┃╱╱╱╱╱╱╱╱╱┃╭╮┃╱╱╱╱╱┃┃╱┃┃
// ┃┃╱╰╋━━┳╮╭┳━━╮┃╰╯╰┳╮╭┳━╯┣━╯┣╮╱╭╮
// ┃┃╱╭┫╭╮┃╰╯┃╭╮┃┃╭━╮┃┃┃┃╭╮┃╭╮┃┃╱┃┃
// ┃╰━╯┃╭╮┃┃┃┃╰╯┃┃╰━╯┃╰╯┃╰╯┃╰╯┃╰━╯┃
// ╰━━━┻╯╰┻┻┻┫╭━╯╰━━━┻━━┻━━┻━━┻━╮╭╯
// ╱╱╱╱╱╱╱╱╱╱┃┃╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╭━╯┃
// ╱╱╱╱╱╱╱╱╱╱╰╯╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╰━━╯
//
//
// ECE 1022 - Team 64
//
// Team Members:
// Andrew Voigt, Caden Tran, Cameron Long, Nathan Meador
//
//
/************************************************************************************/
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <string.h>
#include <Wire.h>
// (CONSTANT) (GLOBAL) Width of the I2C screen
const byte SCREEN_WIDTH = 20;
// (CONSTANT) (GLOBAL) Height of the I2C screen
const byte SCREEN_HEIGHT = 4;
// Bounds Data Structure (double low; double high)
struct Bounds
{
// Lower bound
double low;
// Upper bound
double high;
};
// UI States
enum UIState
{
// Starting page | Displays current internal temperature and humidity | 'A'=MAIN_PAGE_2; 'B'=SETTINGS_PAGE_1
MAIN_PAGE_1,
// Displays current external temperature and humidity | 'A'=MAIN_PAGE_1; 'B'=SETTINGS_PAGE_1
MAIN_PAGE_2,
// Displays options to change temperaute and humidity bounds | 'A'=TEMP_INT_SETTINGS; 'B'=HUMID_INT_SETTINGS; 'C'=SETTINGS_PAGE_2; 'D'=MAIN_PAGE_1
SETTINGS_PAGE_1,
// Displays options to change priority and alarm settings | 'A'=PRIORITY_SETTINGS; 'B'=ALARM_SETTINGS; 'C'=SETTINGS_PAGE_1; 'D'=MAIN_PAGE_1
SETTINGS_PAGE_2,
// Displays options to change upper and lower bounds of internal temperature | 'A'=INPUT_STATE; 'B'=INPUT_STATE; 'C'=TEMP_EXT_SETTINGS; 'D'=SETTINGS_PAGE_1
TEMP_INT_SETTINGS,
// Displays options to change upper and lower bounds of external temperature | 'A'=INPUT_STATE; 'B'=INPUT_STATE; 'C'=TEMP_INT_SETTINGS; 'D'=SETTINGS_PAGE_1
TEMP_EXT_SETTINGS,
// Displays options to change upper and lower bounds of internal humidity | 'A'=INPUT_STATE; 'B'=INPUT_STATE; 'C'=HUMD_EXT_SETTINGS; 'D'=SETTINGS_PAGE_1
HUMID_INT_SETTINGS,
// Displays options to change upper and lower bounds of external humidity | 'A'=INPUT_STATE; 'B'=INPUT_STATE; 'C'=HUMD_INT_SETTINGS; 'D'=SETTINGS_PAGE_1
HUMID_EXT_SETTINGS,
// Displays option to change priority of the EMS | 'A'=Change priority; 'B'=SETTINGS_PAGE_2
PRIORITY_SETTINGS,
// Displays option to change alarm type | 'A'=Change type; 'B'=SETTINGS_PAGE_2
ALARM_SETTINGS,
// Records and displays user input | Accepts 0-9; 'D'=returnState
INPUT_STATE,
// Displays warning that alarm has been triggered and why | 'D'=returnState; 'D'=alarm off
ALARM_STATE
};
// (GLOBAL) Current state
UIState state = MAIN_PAGE_1;
// (GLOBAL) State to return to
UIState returnState = MAIN_PAGE_1;
// (GLOBAL) Character array buffer used for user input
char inputBuf[8];
// (GLOBAL) Index to start printing user input at
int inputIdx = 0;
// (GLOBAL) Pointer used to pass user input by refernce to environment variable being changed
double *inputVal;
// (GLOBAL) Variable used to determine which environment variable should be prioritized in the case that multiple values are out of user bounds
String priority = "TEMP";
// (GLOBAL) Variable used for alarm type | 0=off, 1=motion, 2=environment, 3=both
int alarm_on = 0;
// (GLOBAL) Pointer used to display alarm settings
const char *alarm_str[] = {
"OFF", // 0
"MOTION", // 1
"ENVIRONMENT", // 2
"BOTH" // 3
};
// (GLOBAL) User-defined internal temperature bounds
Bounds internal_temp;
// (GLOBAL) User-defined external temperature bounds
Bounds external_temp;
// (GLOBAL) User-defined internal humidity bounds
Bounds internal_humid;
// (GLOBAL) User-defined external humidity bounds
Bounds external_humid;
// (GLOBAL) System-defined extreme temperature bounds to determine sensor failure
Bounds extreme_temp = {-40, 150};
// (GLOBAL) System-defined extreme humidity bounds to determine sensor failure
Bounds extreme_humid = {0, 100};
// (GLOBAL) Current internal temperature environment variable
double currItemp = 0.0;
// (GLOBAL) Current internal humidity environment variable
double currIhumid = 0.0;
// (GLOBAL) Current external temperature environment variable
double currEtemp = 0.0;
// (GLOBAL) Current external humidity environment variable
double currEhumid = 0.0;
// (GLOBAL) Variable used to determine if alarm has been triggered
bool alarmActive = false;
// (GLOBAL) Variable used to determine the reason for the alarm | 1=motion, 2=user bounds, 3=extreme bounds
int alarmReason = 0;
// (GLOBAL) Variable used for fan speed in percent
double fanSpeed = 0.0;
// (GLOBAL) Variable used for fan direction
int fanDirection = 0;
// (GLOBAL) Variable used to track last fan speed in percent
double lastFanSpeed = 0;
// (GLOBAL) Variable used to track last fan direction
int lastFanDir = 0;
// (CONSTANT) (GLOBAL) Max speed of fan in RPM
const double MAX_SPEED = 255;
// (GLOBAL) Step (in percent) to draw up/down fan
double step = 0.01;
// (GLOBAL) I2C Display
LiquidCrystal_I2C lcd(0x27, SCREEN_WIDTH, SCREEN_HEIGHT);
// (GLOBAL) 2D Character array buffer used to track and print to I2C display
char screenBuffer[SCREEN_HEIGHT][SCREEN_WIDTH + 1];
// (CONSTANT) (GLOBAL) Number of keypad rows
const byte KEYPAD_ROWS = 4;
// (CONSTANT) (GLOBAL) Number of keypad columns
const byte KEYPAD_COLS = 4;
// (GLOBAL) 2D Character array used to define keypad inputs
char keys[KEYPAD_ROWS][KEYPAD_COLS] =
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
// (GLOBAL) Byte array used to define keypad row pins
byte rowPins[KEYPAD_ROWS] = {12,8,7,6};
// (GLOBAL) Byte array used to define keypad column pins
byte colPins[KEYPAD_COLS] = {5,4,3,2};
// (GLOBAL) Keypad
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, KEYPAD_ROWS, KEYPAD_COLS);
// Fan
// (CONSTANT) (GLOBAL) Fan pin 1
const byte MOTOR_PIN_1 = 9;
// (CONSTANT) (GLOBAL) Fan pin 2
const byte MOTOR_PIN_2 = 10;
// EMS Sensors
// (CONSTANT) (GLOBAL) Internal Temperature Sensor pin
const byte TEMP_PIN_1 = A0;
// (CONSTANT) (GLOBAL) Internal Humidity Sensor pin
const byte HUMID_PIN_1 = A1;
// (CONSTANT) (GLOBAL) External Temperature Sensor pin
const byte TEMP_PIN_2 = A2;
// (CONSTANT) (GLOBAL) External Temperature Sensor pin
const byte HUMID_PIN_2 = A3;
// (CONSTANT) (GLOBAL) Variable defining power
const float VCC = 5.0;
// (CONSTANT) (GLOBAL) Analog to Digital conversion max
const int ADC_MAX = 1023;
// (CONSTANT) (GLOBAL) 10k resistor value (ohms)
const float R_FIXED = 10000.0;
// (CONSTANT) (GLOBAL) Thermosistor constant
const float BETA = 3950.0;
// (CONSTANT) (GLOBAL) 25°C in Kelvin
const float T0 = 298.15;
// (CONSTANT) (GLOBAL) 10k oms at 25°C
const float R0 = 10000.0;
// MDS Sensor
// (CONSTANT) (GLOBAL) Passive Infrared Sensor pin
const byte PIR = 13;
// Buzzer
// (CONSTANT) (GLOBAL) Passive Buzzer pin
const byte PASSIVE_BUZZER = 9;
// Preamble
// Environmental Monitoring Subsystem | Determines if current environment variables are within range and calculates fan speed
double EMS(Bounds, Bounds, Bounds, Bounds, UIState* state);
// Noise Reduction Fuction | Takes average of 20 inputs from sensors
int readAverage(byte pin);
// Calculates humidity from sensor input
float readHumidity(byte pin);
// Calculates temperature in Fahrenheit from sensor input
float readTemperatureF(byte pin);
// Air Filtration Subsystem | Takes in speed and direction and turns on the fans
int AFS(double speed, int direction);
// Motion Detection Subsystem | Detects motion and turns on the alarm state according to user settings
int MDS(UIState state);
// Updates the current environment variables
void readSensors();
// Updates the fan speed according to the EMS
void updateAFS();
// Sets the alarm state according to the MDS
void updateAlarm();
// Plays sound on buzzer
void playTune();
// (STATE MACHINE) Updates the display according to the state
void drawScreen();
// Updates specified line of display with inputted character array
void writeLine(byte row, const char* text);
// Map one numerical span to another with floating point values | https://github.com/ninetreesdesign/mapFloat/blob/master/ds_mapFloat/ds_mapFloat.ino
double mapFloat (double x, double in_min, double in_max, double out_min, double out_max);
// Setup
void setup()
{
Serial.begin(115200);
Wire.begin();
lcd.init();
lcd.backlight();
// Pin Setup
pinMode(MOTOR_PIN_1, OUTPUT);
pinMode(MOTOR_PIN_2, OUTPUT);
pinMode(PASSIVE_BUZZER, OUTPUT);
pinMode(PIR, INPUT);
// Fill screen buffer with null values
for (int i = 0; i < SCREEN_HEIGHT; i++)
{
memset(screenBuffer[i], 0, SCREEN_WIDTH + 1);
}
memset(inputBuf, 0, sizeof(inputBuf));
internal_temp = {50, 70};
external_temp = {32, 100};
internal_humid = {10, 30};
external_humid = {10, 80};
writeLine(1, "LOADING...");
writeLine(2, "PLEASE WAIT");
delay(30000);
}
// Loop
void loop()
{
readSensors();
updateAlarm();
updateAFS();
drawScreen();
// Current key being pressed
char key = keypad.getKey();
// (STATE MACHINE) Logic behind each state | Used for interpreting user input and system variables
switch (state)
{
case MAIN_PAGE_1:
if (key == 'A') state = MAIN_PAGE_2;
else if (key == 'B') state = SETTINGS_PAGE_1;
break;
case MAIN_PAGE_2:
if (key == 'A') state = MAIN_PAGE_1;
else if (key == 'B') state = SETTINGS_PAGE_1;
break;
case SETTINGS_PAGE_1:
if (key == 'A') state = TEMP_INT_SETTINGS;
else if (key == 'B') state = HUMID_INT_SETTINGS;
else if (key == 'C') state = SETTINGS_PAGE_2;
else if (key == 'D') state = MAIN_PAGE_1;
break;
case SETTINGS_PAGE_2:
if (key == 'A') state = PRIORITY_SETTINGS;
else if (key == 'B') state = ALARM_SETTINGS;
else if (key == 'C') state = SETTINGS_PAGE_1;
else if (key == 'D') state = MAIN_PAGE_1;
break;
case TEMP_INT_SETTINGS:
if (key == 'A') { returnState = TEMP_INT_SETTINGS; state = INPUT_STATE; inputVal = &internal_temp.high; memset(inputBuf, 0, sizeof(inputBuf)); inputIdx = 0; }
else if (key == 'B') { returnState = TEMP_INT_SETTINGS; state = INPUT_STATE; inputVal = &internal_temp.low; memset(inputBuf, 0, sizeof(inputBuf)); inputIdx = 0; }
else if (key == 'C') state = TEMP_EXT_SETTINGS;
else if (key == 'D') state = SETTINGS_PAGE_1;
break;
case TEMP_EXT_SETTINGS:
if (key == 'A') { returnState = TEMP_EXT_SETTINGS; state = INPUT_STATE; inputVal = &external_temp.high; memset(inputBuf, 0, sizeof(inputBuf)); inputIdx = 0; }
else if (key == 'B') { returnState = TEMP_EXT_SETTINGS; state = INPUT_STATE; inputVal = &external_temp.low; memset(inputBuf, 0, sizeof(inputBuf)); inputIdx = 0; }
else if (key == 'C') state = TEMP_INT_SETTINGS;
else if (key == 'D') state = SETTINGS_PAGE_1;
break;
case HUMID_INT_SETTINGS:
if (key == 'A') { returnState = HUMID_INT_SETTINGS; state = INPUT_STATE; inputVal = &internal_humid.high; memset(inputBuf, 0, sizeof(inputBuf)); inputIdx = 0; }
else if (key == 'B') { returnState = HUMID_INT_SETTINGS; state = INPUT_STATE; inputVal = &internal_humid.low; memset(inputBuf, 0, sizeof(inputBuf)); inputIdx = 0; }
else if (key == 'C') state = HUMID_EXT_SETTINGS;
else if (key == 'D') state = SETTINGS_PAGE_1;
break;
case HUMID_EXT_SETTINGS:
if (key == 'A') { returnState = HUMID_EXT_SETTINGS; state = INPUT_STATE; inputVal = &external_humid.high; memset(inputBuf, 0, sizeof(inputBuf)); inputIdx = 0; }
else if (key == 'B') { returnState = HUMID_EXT_SETTINGS; state = INPUT_STATE; inputVal = &internal_humid.low; memset(inputBuf, 0, sizeof(inputBuf)); inputIdx = 0; }
else if (key == 'C') state = HUMID_INT_SETTINGS;
else if (key == 'D') state = SETTINGS_PAGE_1;
break;
case PRIORITY_SETTINGS:
if (key == 'A') priority = (priority == "TEMP") ? "HUMID" : "TEMP";
else if (key == 'B') state = SETTINGS_PAGE_2;
break;
case ALARM_SETTINGS:
if (key == 'A')
{
++alarm_on;
if (alarm_on > 3) alarm_on = 0;
}
else if (key == 'B') state = SETTINGS_PAGE_2;
break;
case INPUT_STATE:
{
if (key >= '0' && key <= '9' && inputIdx < 7)
{
inputBuf[inputIdx] = key;
++inputIdx;
inputBuf[inputIdx] = '\0';
}
else if (key == 'D')
{
double val = atof(inputBuf);
*inputVal = val;
memset(inputBuf, 0, sizeof(inputBuf));
state = returnState;
}
}
break;
case ALARM_STATE:
playTune();
if (key == 'D') { state = MAIN_PAGE_1; alarm_on = 0; }
break;
}
delay(100);
}
/****************************************/
//
// ╭━━━┳━╮╭━┳━━━╮
// ┃╭━━┫┃╰╯┃┃╭━╮┃
// ┃╰━━┫╭╮╭╮┃╰━━╮
// ┃╭━━┫┃┃┃┃┣━━╮┃
// ┃╰━━┫┃┃┃┃┃╰━╯┃
// ╰━━━┻╯╰╯╰┻━━━╯
//
// Environment Monitoring System
// Author: Nathan Meador
//
double EMS(Bounds iT, Bounds eT, Bounds iH, Bounds eH, UIState* st)
{
if (priority == "TEMP")
{
// Internal temperature too low
if (currItemp < iT.low)
{
if ((alarm_on == 2 || alarm_on == 3) && state != ALARM_SETTINGS) { *st = ALARM_STATE; alarmReason = 2; };
fanDirection = -1;
return mapFloat(iT.low - currItemp, 0.0, extreme_temp.high, 0.0, 100.0);
}
// Internal temperature too high
if (currItemp > iT.high)
{
if ((alarm_on == 2 || alarm_on == 3) && state != ALARM_SETTINGS) { *st = ALARM_STATE; alarmReason = 2; };
fanDirection = 1;
return mapFloat(currItemp - iT.high, 0.0, extreme_temp.high, 0, 100.0);
}
// Internal humidity too high
if (currIhumid > iH.high)
{
if ((alarm_on == 2 || alarm_on == 3) && state != ALARM_SETTINGS) { *st = ALARM_STATE; alarmReason = 2; };
fanDirection = -1;
return mapFloat(currIhumid - iH.high, 0.0, extreme_humid.high, 0.0, 100.0);
}
} else
{
// Internal humidity too high
if (currIhumid > iH.high)
{
if ((alarm_on == 2 || alarm_on == 3) && state != ALARM_SETTINGS) { *st = ALARM_STATE; alarmReason = 2; };
fanDirection = -1;
return mapFloat(currIhumid - iH.high, 0.0, extreme_humid.high, 0.0, 100.0);
}
// Internal temperature too low
if (currItemp < iT.low)
{
if ((alarm_on == 2 || alarm_on == 3) && state != ALARM_SETTINGS) { *st = ALARM_STATE; alarmReason = 2; };
fanDirection = -1;
return mapFloat(iT.low - currItemp, 0.0, extreme_temp.high, 0.0, 100.0);
}
// Internal temperature too high
if (currItemp > iT.high)
{
if ((alarm_on == 2 || alarm_on == 3) && state != ALARM_SETTINGS) { *st = ALARM_STATE; alarmReason = 2; };
fanDirection = 1;
return mapFloat(currItemp - iT.high, 0.0, extreme_temp.high, 0.0, 100.0);
}
}
return 0.0;
}
int readAverage(byte pin)
{
long sum = 0;
for (int i = 0; i < 20; i++)
{
sum += analogRead(pin);
delay(5);
}
return sum / 20;
}
float readHumidity(byte pin)
{
int adc = readAverage(pin);
float voltage = (adc / (float) ADC_MAX) * VCC;
float humidity = (voltage - 0.8) / 3.0 * 100.0;
// Clamp values
if (humidity < 0) humidity = 0;
if (humidity > 100) humidity = 100;
return humidity;
}
float readTemperatureF(byte pin)
{
int adc = readAverage(pin);
if (adc <= 0 || adc >= 1023)
{
return -459.67; // absolute zero in °F
}
float resistance = R_FIXED * ((1023.0 / adc) - 1.0);
float tempK = 1.0 / ((1.0 / T0) + (1.0 / BETA) * log(resistance / R0));
float tempC = tempK - 273.15;
float tempF = (tempC * 9.0 / 5.0) + 32.0;
float m = 0.751;
float b = 16.29;
float calibratedTemp = m * tempF + b;
return calibratedTemp;
}
void readSensors()
{
// Read sensor values
currItemp = (double) readTemperatureF(TEMP_PIN_1);
currIhumid = (double) readHumidity(HUMID_PIN_1);
currEtemp = (double) readTemperatureF(TEMP_PIN_2);
currEhumid = (double) readHumidity(HUMID_PIN_2);
}
// 5 variables: iT, eT, iH, eH, State
//
/****************************************/
/****************************************/
//
// ╭━━━┳━━━┳━━━╮
// ┃╭━╮┃╭━━┫╭━╮┃
// ┃┃╱┃┃╰━━┫╰━━╮
// ┃╰━╯┃╭━━┻━━╮┃
// ┃╭━╮┃┃╱/┃╰━╯┃
// ╰╯╱╰┻╯╱╱╰━━━╯
//
// Air Filtration System
// Author: Caden Tran
//
int AFS(double speed, int direction)
{
if (speed < 0) speed = 0;
if (speed > 100) speed = 100;
if (speed > lastFanSpeed) lastFanSpeed += step;
if (speed < lastFanSpeed) lastFanSpeed -= step;
if (fabs(speed - lastFanSpeed) < step) lastFanSpeed = speed;
double RPM = MAX_SPEED * lastFanSpeed/100;
Serial.print("AFS | Speed: "); Serial.print(RPM); Serial.print(" RMP"); Serial.print(" Direction: "); Serial.println(direction);
Serial.print("AFS | Speed: "); Serial.print(lastFanSpeed); Serial.println(" %");
return 0;
}
void updateAFS()
{
fanSpeed = EMS(internal_temp, external_temp, internal_humid, external_humid, &state);
if ((fanSpeed != lastFanSpeed || fanDirection != lastFanDir) && fanDirection != -1) // fan direction not implemented due to hardware restrictions
{
AFS(fanSpeed, fanDirection);
lastFanSpeed = fanSpeed;
lastFanDir = fanDirection;
}
}
// 2 variables: Speed(percent) and Direction
//
/****************************************/
/****************************************/
//
// ╭━╮╭━┳━━━┳━━━╮
// ┃┃╰╯┃┣╮╭╮┃╭━╮┃
// ┃╭╮╭╮┃┃┃┃┃╰━━╮
// ┃┃┃┃┃┃┃┃┃┣━━╮┃
// ┃┃┃┃┃┣╯╰╯┃╰━╯┃
// ╰╯╰╯╰┻━━━┻━━━╯
//
// Motion Detection System
// Author: Andrew Voigt
//
int MDS(UIState st)
{
if (digitalRead(PIR) == HIGH) return 1; // Motion detected
return 0;
}
void updateAlarm()
{
alarmActive = false;
if ((alarm_on == 1 || alarm_on == 3) && MDS(state) != 0 && state != ALARM_SETTINGS)
{
alarmActive = true;
alarmReason = 1;
state = ALARM_STATE;
}
}
void playTune()
{
tone(PASSIVE_BUZZER, 500, 1000);
}
// 1 variable: State
//
/****************************************/
void drawScreen()
{
char line[21];
char num[8];
switch (state)
{
case MAIN_PAGE_1:
dtostrf(currItemp, 3, 1, num);
snprintf(line, 21,"INTERNAL TEMP:%sF", num);
writeLine(0, line);
dtostrf(currIhumid, 3, 1, num);
snprintf(line, 21,"INTERNAL HUM:%s%%", num);
writeLine(1, line);
writeLine(2,"A:NEXT B:SETTINGS");
writeLine(3, " ");
break;
case MAIN_PAGE_2:
dtostrf(currEtemp, 3, 1, num);
snprintf(line, 21,"EXTERNAL TEMP:%sF", num);
writeLine(0, line);
dtostrf(currEhumid, 3, 1, num);
snprintf(line, 21,"EXTERNAL HUM:%s%%", num);
writeLine(1, line);
writeLine(2,"A:PREV B:SETTINGS");
writeLine(3, " ");
break;
case SETTINGS_PAGE_1:
writeLine(0,"SETTINGS PAGE 1");
writeLine(1,"A:TEMP B:HUMID");
writeLine(2,"C:NEXT D:HOME");
writeLine(3, " ");
break;
case SETTINGS_PAGE_2:
writeLine(0,"SETTINGS PAGE 2");
writeLine(1,"A:PRIORITY");
writeLine(2,"B:ALARM");
writeLine(3,"C:PREV D:HOME");
break;
case TEMP_INT_SETTINGS:
writeLine(0,"INTERAL TEMPERATURE");
dtostrf(internal_temp.high, 3, 1, num);
snprintf(line, 21,"A:UPPER BOUND=%sF", num);
writeLine(1, line);
dtostrf(internal_temp.low, 3, 1, num);
snprintf(line, 21,"B:LOWER BOUND=%sF", num);
writeLine(2, line);
writeLine(3,"C:NEXT D:DONE");
break;
case TEMP_EXT_SETTINGS:
writeLine(0,"EXTERNAL TEMPERATURE");
dtostrf(external_temp.high, 3, 1, num);
snprintf(line, 21,"A:UPPER BOUND=%sF", num);
writeLine(1, line);
dtostrf(external_temp.low, 3, 1, num);
snprintf(line, 21,"B:LOWER BOUND=%sF", num);
writeLine(2, line);
writeLine(3,"C:PREV D:DONE");
break;
case HUMID_INT_SETTINGS:
writeLine(0,"INTERAL HUMIDITY");
dtostrf(internal_humid.high, 3, 1, num);
snprintf(line, 21,"A:UPPER BOUND=%s%%", num);
writeLine(1, line);
dtostrf(internal_humid.low, 3, 1, num);
snprintf(line, 21,"B:LOWER BOUND=%s%%", num);
writeLine(2, line);
writeLine(3,"C:NEXT D:DONE");
break;
case HUMID_EXT_SETTINGS:
writeLine(0,"EXTERNAL HUMIDITY");
dtostrf(external_humid.high, 3, 1, num);
snprintf(line, 21,"A:UPPER BOUND=%s%%", num);
writeLine(1, line);
dtostrf(external_humid.low, 3, 1, num);
snprintf(line, 21,"B:LOWER BOUND=%s%%", num);
writeLine(2, line);
writeLine(3,"C:PREV D:DONE");
break;
case PRIORITY_SETTINGS:
snprintf(line, 21,"PRIORITY:%-6s", priority.c_str());
writeLine(0, "PRIORITY SETTINGS ");
writeLine(1, line);
writeLine(2, "A:TOGGLE B:BACK ");
writeLine(3, " ");
break;
case ALARM_SETTINGS:
snprintf(line, 21, "ALARM:%s", alarm_str[alarm_on]);
writeLine(0, "ALARM SETTINGS ");
writeLine(1, line);
writeLine(2, "A:TOGGLE B:BACK ");
writeLine(3, " ");
break;
case ALARM_STATE:
writeLine(0, " ");
writeLine(1,"!!! ALARM !!!");
if (alarmReason == 1) writeLine(2,"MOTION DETECTED");
else if (alarmReason == 2) writeLine(2,"AFS ACTIVE");
else if (alarmReason == 3) writeLine(2,"INVALID SENSOR");
else writeLine(2,"REASON UNKNOWN");
writeLine(3,"D:TURN ALARM OFF");
break;
case INPUT_STATE:
writeLine(0, " ");
writeLine(1,"INPUT:");
snprintf(line, 21,"%s", inputBuf);
writeLine(2, line);
writeLine(3,"D:DONE");
default:
break;
}
}
void writeLine(byte row, const char* text)
{
if (strcmp(screenBuffer[row], text) != 0)
{
lcd.setCursor(0,row);
lcd.print(" ");
lcd.setCursor(0,row);
lcd.print(text);
strncpy(screenBuffer[row], text, SCREEN_WIDTH);
screenBuffer[row][SCREEN_WIDTH] = '\0';
}
}
double mapFloat (double x, double in_min, double in_max, double out_min, double out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
Internal Humidity
Internal Temperature
External Humidity
External Temperature