//-------------------Sound Alarm-----1-25-2024------------------------------------------------------
//==================Librarys============================================================================
#include <LiquidCrystal_I2C.h>
//==================Constant and Global Declarations====================================================
//------1602 I2C Module Inland KN9B LCD Display-----------------
const byte lcdSDA = A4;
const byte lcdSCL = A5;
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 column and 2 rows
//------Rotary Encoder Module Inland KN9P------------------------
const byte encCLK = 3;
const byte encDT = 4;
const byte encSW = 5;
//String dir = "";
unsigned long lastRotEncTurn = 0;
unsigned long lastRotEncPress = 0;
byte encSwState = 0;
const int numEncMenuItems = 3;
String encSwText[numEncMenuItems] = {" ", "SQL", "TRIP"};
const byte encSwStateBLANK = 0;
const byte encSwStateSQL = 1;
const byte encSwStateTRIP = 2;
byte encRfmdSwState = 0;
const int numRfmdEncMenuItems = 3;
String encRfmdSwText[numRfmdEncMenuItems] = {" ", "SQL", "MODE"};
const byte encRfmdSwStateBLANK = 0;
const byte encRfmdSwStateSQL = 1;
const byte encRfmdSwStateMODE = 2;
int sqlValue = 100;
int tripValue = 150;
int incSign;//
int rfmdSqlValue = 3;
//-------Mode Pushbutton-Switch----------------------------------
const byte swMode = 13;
unsigned long lastSwModePress = 0;//for debouncing
byte modeState = 0;
const int numModeMenuItems = 5;
String modeText[numModeMenuItems] = {"MUTE", "TONE", "HORN", "RFMD", "TEST" };
const byte modeStateMUTE = 0;
const byte modeStateTONE = 1;
const byte modeStateHORN = 2;
const byte modeStateRFMD = 3;
const byte modeStateTEST = 4;
//--------Horn Test Pushbutton Switch----------------------------
const byte swTest = 2;
unsigned long lastSwTestPress = 0;//for debouncing
bool flgHornTest_L = true;//Press button to sound horn
String hornTestText[2] = {"ON ", "OFF"};
//---------Sound Detector Sparkfun SEN-12642---------------------
const byte sdEnvelope = A1;
//----------Audio Indicator Projects Unlimited AI-550KS ---------
const byte ai550 = 6;
//----------Relay Module Inland KN5T SKU 509687--------------------
const byte ryIN = 8;
//-------------ADC Value-------------------------------------------
int adcAvgValue = 0;
//------------Pushbutton Debounce Delay-----------------------------
byte swDebounceDelay = 100; //ms
//------------------Horn Blow Parameters-----------------------------
const int hbOnTimeSec = 2;
const int hbOffTimeSec = 2;
const int hbNumBlows = 5;
bool hbTrigger = true;
//---------Microwave Motion Sensor------------------------------------
const byte rfmd = 12; //RFMD Sensor input
byte rfmdTripValue = 0;
byte rfmdModeState = 0;
const int numRfmdModeMenuItems = 3;
String rfmdModeText[numRfmdModeMenuItems] = {"MUTE", "TONE", "HORN"};
const byte rfmdModeStateMUTE = 0;
const byte rfmdModeStateTONE = 1;
const byte rfmdModeStateHORN = 2;
//-------------RFMD--Horn Blow Parameters-----------------------------
const int rfmdOnTimeSec = 2;
const int rfmdOffTimeSec = 2;
const int rfmdNumBlows = 2;
bool rfmdTrigger = false;
//END Constant and Global Declarations
//---------------Serial Port Enable Pin--------------------------------
const byte serialPortEnable_L=11;//Ground Pin to start Serial port det audio acq in Mute Mode
//---------------Setup---------------------------------------------------------------------------------
void setup() {
//-----------Serial Port----------------------------------------
Serial.begin(9600);
//------1602 I2C Module Inland KN9B LCD Display-----------------
lcd.init(); //initialize the lcd
lcd.backlight(); //open the backlight
lcd.clear(); // clear display
//------Rotary Encoder Module Inland KN9P------------------------
pinMode(encCLK, INPUT);
pinMode(encDT, INPUT);
pinMode(encSW, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(encCLK), isrRotEncTurned, FALLING);
//---Rotary Encoder Switch Pin Change Interrupt
PCICR |= B00000100; //Enable PCMSK2 (Group 2: PCINT23 to PCINT16 <--> D7 to D0; Port D)
//PCMSK2: Pin Change Mask Register 2 (Group 2)
PCMSK2 |= B00100000; // D5 PD5 PCINT21 will trigger interrupt
//-------Mode Pushbutton Switch----------------------------------
pinMode(swMode, INPUT_PULLUP);
PCICR |= B00000001; //Enable PCMSK0 (Group 0: PCINT5 to PCINT0 <--> D13 to D8; Port B)
//PCMSK0: Pin Change Mask Register 0 (Group 0)
PCMSK0 |= B00110000; // D13 & D12 will trigger interrupts D13(Mode) D12(RFMD Out)
//Serial.print("PCICR: "); Serial.println(PCICR, BIN);
//--------Horn Test Pushbutton Switch----------------------------
pinMode(swTest, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(swTest), isrSwTestPressed, FALLING);
//---------Sound Detector Sparkfun SEN-12642---------------------
pinMode(sdEnvelope, INPUT);
//----------Audio Indicator Projects Unlimited AI-550KS ---------
pinMode(ai550, OUTPUT);
digitalWrite(ai550, LOW);//Low is OFF
//----------Relay Module Inland KN5T SKU 509687--------------------
pinMode(ryIN, OUTPUT);
digitalWrite(ryIN, LOW);
//----------RFMD--------------------------------------------------
pinMode(rfmd, INPUT);
digitalWrite(rfmd, LOW);
//------Serial Port Data Acq-----------------------------
pinMode(serialPortEnable_L, INPUT_PULLUP);
}//END Setup
//================================SUBROUTINES================================================================
//-------LCD Print Enc and Mode State (Line 1)-------------------------
void fPrintState() {
String line1;
//char buffer[16];
if (modeState != modeStateRFMD) {
switch (encSwState) {
case encSwStateBLANK:
if (modeState == modeStateTEST) {
line1 = modeText[modeState] + " " + hornTestText[flgHornTest_L] + " " + encSwText[encSwState];
} else {
line1 = modeText[modeState] + " " + encSwText[encSwState] + " " + encSwText[encSwState];
}
break;
case encSwStateSQL:
line1 = modeText[modeState] + " " + encSwText[encSwState] + " ";
break;
case encSwStateTRIP:
line1 = modeText[modeState] + " " + encSwText[encSwState];
break;
case modeStateRFMD:
line1 = modeText[rfmdModeState] + " " + encRfmdSwText[encRfmdSwState];
default:
line1 = modeText[modeState] + " ???? ????";
break;
}//END switch (encSwState)
} else {
switch (encRfmdSwState) {
case encRfmdSwStateBLANK:
if (modeState == modeStateTEST) {
line1 = modeText[modeState] + " " + hornTestText[flgHornTest_L] + " " + encSwText[encSwState];
} else {
line1 = modeText[modeState] + " " + encRfmdSwText[encRfmdSwState] + " " + encRfmdSwText[encRfmdSwState];
}
break;
case encRfmdSwStateSQL:
line1 = modeText[modeState] + " " + encRfmdSwText[encRfmdSwState] + " ";
break;
case encRfmdSwStateMODE:
line1 = modeText[modeState] + " " + encRfmdSwText[encRfmdSwState];
break;
default:
line1 = modeText[modeState] + " ???? ????";
break;
}//END switch (encRfmdSwState)
}//END IF
lcd.setCursor(0, 1); lcd.print(line1); //Write line1 to Display (col,row)
}//fPrintState
//-------LCD Print ADC Squelch and Trip Values (Line 0)-------------------------
void fPrintValues() {
lcd.setCursor(0, 0); lcd.print(" "); // Clear entire Line
if (modeState != modeStateRFMD) {
lcd.setCursor(0, 0); lcd.print(adcAvgValue);
lcd.setCursor(6, 0); lcd.print(sqlValue);//(col,row)
lcd.setCursor(12, 0); lcd.print(tripValue); //(col,row);
} else {
lcd.setCursor(0, 0); lcd.print(rfmdTripValue);
lcd.setCursor(6, 0); lcd.print(rfmdSqlValue);//(col,row)
lcd.setCursor(12, 0); lcd.print(rfmdModeText[rfmdModeState]); //(col,row);
}
}//End fPrintValues
// --------------------------Start adcAcquire----------------------------------------------
int adcAcquire() {
// --------------------------ADMUX--Table 23-3, 23-4----------------
// | REFS1 | REFS0 | ADLAR | - | MUX3 | MUX2 | MUX1 | MUX0 |
// ----------------------------------------------------------------
// | 0 1 | 0 | 0 | 0 0 0 1 |
// | AVcc Ref | ADC | | Read A1 |
// |Rt Just|
// -----------------------------------------------------------------
//Read A4 (MUX2)
ADMUX |= B00000001;//_BV(MUX2); / Read A1
//Select Voltage Reference
ADMUX |= B01000000;// REFS1=0 AND REFS0=1; AVcc Ref
// --------------------------ADCSRA--Table 23-5, 23-4----------------
// | ADEN | ADSC | ADATE | ADIF | ADIE | ADPS2 | ADPS1 | ADPS0 |
// ----------------------------------------------------------------
// | 1 | 1 0 | 0 | 0 | 1 1 1 |
// | | | | ADC CLK =16MHz/128 |
// -----------------------------------------------------------------
//Start AD Conversion
ADCSRA |= B11000000;//_BV(ADEN) | _BV(ADSC); ADEC and ADSC equal to 1
//Set Prescaler
ADCSRA |= B00000111;// ASPS2:ADPS0 111--> divided by 128 100 --> divide by 16
//Wait for end-of-conversion
while (bit_is_set(ADCSRA, ADSC)); //ADSC: 1 Conversion in progress; ADSC: 0 Conversion Done
//Get the Analog Read Value
int value = ADCL | (ADCH << 8);
return value;
}//END adcAcquire
// --------------------------Start ADC Acquire and Avg--------------------------------------
int adcAvg(int hbLoopTime) {
long val = 0;
long rawVal = 0;
int countSamples = 0;
int rawCountSamples = 0;
int nextVal;
unsigned long startTime = 0;
startTime = millis();
while (millis() - startTime <= hbLoopTime) {
nextVal = adcAcquire();
if (modeState == modeStateMUTE && digitalRead(serialPortEnable_L)==LOW) {
Serial.println(nextVal); //Send raw data only in MUTE
}
//Process all Samples
rawVal = rawVal + nextVal;
rawCountSamples++;
//Process ADC Samples above Threshold
if (nextVal >= sqlValue) {
val = val + nextVal; //Sum only values above sqlValue
countSamples++;
}
}
if (countSamples == 0) { //No samples in average
val = rawVal / rawCountSamples; //Return All Sample Average Value
}
else {//Return Thresholded Average Value
val = val / countSamples;//unsigned long will truncate to integer
}
return val;
}//End adcAvg
//-----------------Horn Blow ON---------------------------------------------------------
void fHornBLowON(byte N) {
int hbLoopTime = 1000 / N;
if (hbTrigger == 0 ) {
return;
}
for (int j = 1; j <= hbOnTimeSec * N; j++) {
fPrintValues();
fPrintState();
switch (modeState) {
case modeStateMUTE:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
case modeStateTONE:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, HIGH);//Audio indcator LOW:OFF HiGH:ON
break;
case modeStateHORN:
digitalWrite(ryIN, HIGH);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
case modeStateTEST:
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
if (flgHornTest_L == false) {
digitalWrite(ryIN, HIGH);// Horn Relay control LOW:OFF HiGH:ON
}
else {
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
}
break;
default:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
}
delay(hbLoopTime);
}
}//End fHornBLowON
//---------------------Horn Blow OFF-----------------------------------------------------------------
void fHornBLowOFF(byte N) {
int avg = 0; //reset average
int hbLoopTime = 1000 / N;
int j = 0; //allow access outside for loop
for (j = 1; j <= hbOffTimeSec * N; j++) {
fPrintValues();
fPrintState();
switch (modeState) {
case modeStateMUTE:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
case modeStateTONE:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
case modeStateHORN:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
case modeStateTEST:
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
if (flgHornTest_L == false) {
digitalWrite(ryIN, HIGH);// Horn Relay control LOW:OFF HiGH:ON
}
else {
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
}
break;
default:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
}
avg = avg + adcAvg(hbLoopTime); //does delay(hbLoopTime);
}//END For
adcAvgValue = avg / (j - 1); //Calculate total average during off time
if (adcAvgValue >= tripValue ) {
hbTrigger = 1;
} else {
hbTrigger = 0;
}
}//END fHornBLowOFF
//--------------------Horn Blow Routine-------------------------------------------------------------
void fBlowHorn() {
if (modeState == modeStateRFMD) {
return;
}
//Serial.println("fBlowHorn(): 377");
byte N = 1; // ON/OFF Loop Delay Time Scale Factor
for (int i = 0; i < hbNumBlows; i++) {
fHornBLowON(N);
fHornBLowOFF(N);
}//Number of Blows
}//End fBlowHorn()
//-----------------------RFMD Blow Horn OFF---------------------------------------------------------
void fRfmdBlowHornOFF(byte N) {
int rfmdLoopTime = 1000 / N;
//Serial.print("rfmdTrigger: 387: "); Serial.println(rfmdTrigger);
if (modeState != modeStateRFMD || rfmdTrigger == false) {
//if (modeState != modeStateRFMD ) {
return;
}
int j = 0; //allow access outside for loop
for (j = 1; j <= rfmdOffTimeSec * N; j++) {
fPrintValues();
fPrintState();
switch (rfmdModeState) {
case rfmdModeStateMUTE:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
case rfmdModeStateTONE:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
case rfmdModeStateHORN:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
default:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
}
delay(rfmdLoopTime); //does delay(hbLoopTime);
}//END For
}//END fRfmdBlowHornOFF()
//-----------------------RFMD Blow Horn ON---------------------------------------------------------
void fRfmdBlowHornON(byte N) {
int rfmdLoopTime = 1000 / N;
// Serial.print("rfmdTrigger: 450: "); Serial.println(rfmdTrigger);
if (modeState != modeStateRFMD || rfmdTrigger == false) {
fPrintValues();
fPrintState();
return;
}
//Serial.println("fRfmdBlowHornON(byte N): 451");
//Serial.print("fRfmdBlowHornON(byte N): 452: "); Serial.println(rfmdModeState);
//Serial.print("rfmdTrigger: 447: ");Serial.println(rfmdTrigger);
for (int j = 1; j <= rfmdOnTimeSec * N; j++) {
fPrintValues();
fPrintState();
switch (rfmdModeState) {
case rfmdModeStateMUTE:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
case rfmdModeStateTONE:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, HIGH);//Audio indcator LOW:OFF HiGH:ON
break;
case rfmdModeStateHORN:
digitalWrite(ryIN, HIGH);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
default:
digitalWrite(ryIN, LOW);// Horn Relay control LOW:OFF HiGH:ON
digitalWrite(ai550, LOW);//Audio indcator LOW:OFF HiGH:ON
break;
}//End Switch
delay(rfmdLoopTime);
}//End For
}//END fRfmdBlowHorn()
// --------------------RFMD Horn Blow Routine------------------------------------------------------------ -
void fRfmdBlowHorn() {
if (modeState != modeStateRFMD) {
fPrintValues();
fPrintState();
return;
}
//--Determine Trigger State----
if (rfmdTripValue >= rfmdSqlValue) {
rfmdTrigger = true;
//Serial.print("rfmdTrigger: ");Serial.println(rfmdTrigger);
} else {
rfmdTrigger = false;
}
//Serial.println("fRfmdBlowHorn(): 517");
byte N = 1; // ON/OFF Loop Delay Time Scale Factor
for (int i = 0; i < rfmdNumBlows; i++) {
fRfmdBlowHornON(N);
fRfmdBlowHornOFF(N);
delay(1000);
}//Number of Blows
if (rfmdTrigger == true) {
rfmdTripValue = 0;//Clear Detected motion after alarm
}
}//End fBlowHorn()
//======================================MAIN LOOP========================================================
void loop() {
fBlowHorn();
fRfmdBlowHorn();
delay(1000);
}//END Loop
//===========================Intrupt Service Routines=====================================================
//https://projecthub.arduino.cc/mdraber/using-rotary-encoders-with-arduino-interrupts-b59368
//----Rotary Encoder Hardware Interrupt Service Routine---------------------------------
void isrRotEncTurned() {
//byte incSign;
if (millis() - lastRotEncTurn > swDebounceDelay) {
if (digitalRead(encDT) == 1) {
incSign = +1;
//dir = "CW";
}
if (digitalRead(encDT) == 0) {
incSign = -1;
//dir = "CCW";
}
lastRotEncTurn = millis();
}
if (modeState != modeStateRFMD) {
switch (encSwState) {
case 1: //"SQL"
sqlValue = sqlValue + incSign;
sqlValue = constrain(sqlValue, 0, 1023);
break;
case 2: //"TRIP"
tripValue = tripValue + incSign;
tripValue = constrain(tripValue, 0, 1023);
break;
default:
break;
}//Ebd encSwState
} else {//modeState==modeStateRFMD
switch (encRfmdSwState) {
case encRfmdSwStateSQL:
rfmdSqlValue = rfmdSqlValue + incSign;
rfmdSqlValue = constrain(rfmdSqlValue, 0, 1023);
break;
case encRfmdSwStateMODE:
// Add modulo numRfmdModeMenuItems to keep rfmdModeState positive
rfmdModeState = (rfmdModeState + incSign + numRfmdModeMenuItems) % numRfmdModeMenuItems;
break;
default:
break;
}//END switch(encRfmdSwState)
}//END ELSE
}//END isrRotEncTurned()
//----Test Pushputton Hardware Interrupt Service Routine---------------------------------
void isrSwTestPressed() {
//Serial.println("isrSwTestPressed()");
if (millis() - lastSwTestPress > swDebounceDelay) {
if (digitalRead(swTest) == 0 && modeState == modeStateTEST && encSwState == encSwStateBLANK) {
flgHornTest_L = !flgHornTest_L;
}
else {
flgHornTest_L = true; //turn off
}
}
lastSwTestPress = millis();
}
//---------ISR for Mode Pushbutton--& RFMD Sensor In-------------------------------------------------
// ISR for Port B (D13:D8) PCINT0_vect: Vector generated from interrupt
ISR (PCINT0_vect) {
// Serial.print("digitalRead(rfmd): ");Serial.println(digitalRead(rfmd));
if (millis() - lastSwModePress > swDebounceDelay) {
if (digitalRead(swMode) == 0) {
modeState = (modeState + 1) % numModeMenuItems;
}
//Turn off horn test if on
//Only allow horn test to be on in Test Mode
if (modeState != modeStateTEST) {
flgHornTest_L = true;//Turn Off Relay
}
lastSwModePress = millis();
}//End lastSwModePress
if (digitalRead(rfmd) == 1 && modeState == modeStateRFMD) {
//Serial.print("digitalRead(rfmd) 2: "); Serial.println(digitalRead(rfmd));
rfmdTripValue++;
}
}//ISR (PCINT0_vect)
//---------ISR for Rotary Encoder Pushbutton---------------------------------------------
// ISR for Port D (D7:D0) PCINT2_vect: Vector generated from interrupt encSwState
ISR (PCINT2_vect) {
//Serial.println("ISR (PCINT2_vect) line 620");
//Serial.print("621 modeState: "); Serial.println(modeState);
if (millis() - lastRotEncPress > swDebounceDelay) {
if (digitalRead(encSW) == 0) {
if (modeState != modeStateRFMD) {
encSwState = (encSwState + 1) % numEncMenuItems;
// Serial.print("626 encSwState: "); Serial.println(encSwState);
}
//Serial.println("629 ISR (PCINT2_vect)");
//Serial.print("630 modeState == modeStateRFMD: "); Serial.println(modeState == modeStateRFMD);
if (modeState == modeStateRFMD) {
encRfmdSwState = (encRfmdSwState + 1) % numRfmdEncMenuItems;
// Serial.print("633 encRfmdSwState: "); Serial.println(encRfmdSwState);
}//End Mode State
}//End IF digtal Read
if (encSwState != encSwStateBLANK || encRfmdSwState != encRfmdSwStateBLANK) {
flgHornTest_L = true;//Turn Off Relay
}
lastRotEncPress = millis();
}
}