/* Sketch uses 3984 bytes (12%) of program storage space. Maximum is 32256 bytes.
Global variables use 53 bytes (2%) of dynamic memory,
leaving 1995 bytes for local variables. Maximum is 2048 bytes.
telnum 0 = beacon off (white or blue)
telnum 1 = beacon on (white or blue)
telnum 2 = beacon flash (amber) // not working
telnum 3 = beacon pulse (white or blue) // not working, hangs in endless loop
telnum 4 = beacon colour white
telnum 5 = beacon colour blue
2024-01-20 48:17 // glitchy when first dialling after power-on, and when exiting PULSE
*/
// --- Declarations and Variables --- Phone ---
const byte phoneHookPin = 0;
const byte phoneSpoolPin = 1;
const byte phonePulsePin = 2;
const byte phoneHookLED = 3;
const byte phoneSpoolLED = 4;
const byte phonePulseLED = 5;
const byte phoneState_HOOK_READY_LED = 6;
const byte phoneState_DIAL_TONE_LED = 7;
const byte phoneState_DIALS_NUM_LED = 8;
const byte phoneState_CONNECTED_LED = 9;
const byte beaconBluePin = 10;
const byte beaconWhitePin = 11;
const byte beaconAmberPin = 12;
bool ringPhone = false;
byte newHookPinReading;
byte oldHookPinReading;
byte newSpoolPinReading;
byte newPulsePinReading;
byte oldPulsePinReading;
unsigned long timeStamp;
unsigned long currentTime;
unsigned long noActivityStartTime;
const unsigned long ringerTimeExpired = 15000;
const unsigned long debounceDelay = 5;
const unsigned long maxPulseInterval = 250;
const unsigned long maxDiallingPeriod = 1000;
const int telNumLength = 1;
char telNumArray[telNumLength + 1];
byte currentDigitCount;
byte pulsesCounted;
enum phoneListedStates { HOOK_RESET, HOOK_READY, HOOK_RINGING, DIAL_TONE, DIALS_NUM, ANSWERED, CONNECTED };
enum phoneListedStates nextPhoneState;
byte lastPhoneState;
bool incomingRing = false;
// --- Declarations and Variables --- Beacon ---
enum beaconListedColours { WHITE, BLUE };
enum beaconListedColours beaconNextColour;
byte beaconOutputPin;
enum beaconListedModes {ON, OFF, PULSE, FLASH} ;
enum beaconListedModes nextBeaconMode;
unsigned long beaconCurrentTime;
unsigned long beaconTimeStamp;
const int beaconPulseCycleLength = 10;
int beaconLoopCount;
bool beaconBrightness; // for LED output pins
// --- Prototypes --- Phone ---
void phone_Setup();
void phone_Hook_Pin_State();
void phone_State_HOOK_RESET();
void phone_State_HOOK_READY();
void phone_State_DIAL_TONE();
void phone_State_DIALS_NUM();
void phone_State_CONNECTED();
void phone_Set_State_LED_Indicators(byte brightness);
void switch_Phone_States();
// --- Prototypes --- Beacon ---
void beacon_Setup();
void beacon_Pulse_Timing();
void beaconSwitchColour();
void beacon_Switch_Modes();
void beacon_Mode_ON_or_OFF();
void beacon_Mode_PULSE();
void beacon_Mode_FLASH();
// --- Prototypes --- The Big Picture ---
void setup();
void loop();
// --- Functions --- Phone ---
void phone_Setup() {
pinMode(phoneHookPin, INPUT_PULLUP);
pinMode(phoneSpoolPin, INPUT_PULLUP);
pinMode(phonePulsePin, INPUT_PULLUP);
pinMode(phoneHookLED, OUTPUT);
pinMode(phoneSpoolLED, OUTPUT);
pinMode(phonePulseLED, OUTPUT);
pinMode(phoneState_HOOK_READY_LED, OUTPUT);
pinMode(phoneState_DIAL_TONE_LED, OUTPUT);
pinMode(phoneState_DIALS_NUM_LED, OUTPUT);
pinMode(phoneState_CONNECTED_LED, OUTPUT);
currentTime = millis();
timeStamp = currentTime;
delay(5); // to debounce HookPin
newHookPinReading = digitalRead(phoneHookPin);
if (newHookPinReading == 1) {
nextPhoneState = HOOK_RESET;
phone_State_HOOK_RESET();
}
if (newHookPinReading == 0) {
nextPhoneState = DIAL_TONE;
phone_State_DIAL_TONE();
}
}
void phone_Hook_Pin_State() {
currentTime = millis();
if (currentTime - timeStamp < debounceDelay) {
return;
}
oldHookPinReading = newHookPinReading;
newHookPinReading = digitalRead(phoneHookPin);
digitalWrite(phoneHookLED, !newHookPinReading); // illuminate HookLED when HookPin is at LOW ie: "OFF_HOOK"
if ((oldHookPinReading == 0) && (newHookPinReading == 0)) { // remains in current "off hook" state
return;
}
// end of 0/01
if ((oldHookPinReading == 0) && (newHookPinReading == 1)) { // change from any/all "off hook" states to "on hook reset" state
nextPhoneState = HOOK_RESET;
return;
}
// end of 0/1
if ((oldHookPinReading == 1) && (newHookPinReading == 1)) {
if (incomingRing == false) { // "on hook" and "not ringing"
if (lastPhoneState == HOOK_RESET) { // change to "on hook ready" from "on hook reset"
nextPhoneState = HOOK_READY;
return;
}
if (lastPhoneState == HOOK_READY) { // remains in current "on hook" state
return;
}
}
if (incomingRing == true) { // "on hook" and "is ringing"
currentTime = millis();
if (lastPhoneState == HOOK_READY) {
nextPhoneState = HOOK_RINGING;
timeStamp = currentTime; // get fresh timestamp for "ring-no-answer"
return;
}
if (lastPhoneState == HOOK_RINGING) {
if (currentTime - timeStamp < ringerTimeExpired) {
// timer countdown for "ring-no-answer"
return;
}
if (currentTime - timeStamp >= ringerTimeExpired) { // exit countdown for "ring-no-answer"
incomingRing = false; // should be moved into HOOK_RESET function
nextPhoneState = HOOK_RESET; // reset parameters before nextPhoneState = HOOK_READY;
return;
}
}
}
}
// end of 1/1
if ((oldHookPinReading == 1) && (newHookPinReading == 0)) {
if (lastPhoneState == HOOK_READY) {
nextPhoneState = DIAL_TONE;
return;
}
if (lastPhoneState == HOOK_RINGING) {
nextPhoneState = ANSWERED;
return;
}
}
// end of 1/0
} // end of phone_Hook_Pin_State()
void phone_State_HOOK_RESET() {
if (lastPhoneState == nextPhoneState) {
digitalWrite(phoneState_HOOK_READY_LED, 1);
currentDigitCount = 0;
pulsesCounted = 0;
// telNumArray[0] = "/0"; // Will this clear the "telNumArray[]", and is it essential to clear it?
}
}
void phone_State_HOOK_READY() {
// to be determined later
return;
}
void phone_State_DIAL_TONE() {
///////////////////////////////////////////////////////////////////////////////////////////////////////
if (lastPhoneState != nextPhoneState) { // set new timestamp when newly entering DIAL_TONE state //
phone_Set_State_LED_Indicators(0); // //
digitalWrite(phoneState_DIAL_TONE_LED, 1); // //
//currentTime = millis(); // use timeStamp from switch_Phone_States() // //
timeStamp = currentTime; // //
//noActivityStartTime = currentTime; // //
lastPhoneState = nextPhoneState; // //
return; // //
} // //
///////////////////////////////////////////////////////////////////////////////////////////////////////
if (currentTime - timeStamp < debounceDelay) {
return;
}
if (currentTime - timeStamp >= debounceDelay) {
newSpoolPinReading = digitalRead(phoneSpoolPin);
digitalWrite(phoneSpoolLED, !newSpoolPinReading);
if (newSpoolPinReading == 0) {
nextPhoneState = DIALS_NUM;
}
}
}
void phone_State_DIALS_NUM() {
///////////////////////////////////////////////////////////////////////////////////////////////////////
if (lastPhoneState != nextPhoneState) { // set new timestamp when newly entering DIALS_NUM state //
phone_Set_State_LED_Indicators(0); // //
digitalWrite(phoneState_DIALS_NUM_LED, 1); // //
//currentTime = millis(); // use timeStamp from switch_Phone_States() // //
timeStamp = currentTime; // //
//noActivityStartTime = currentTime; // //
lastPhoneState = nextPhoneState; // //
return; // //
} // //
///////////////////////////////////////////////////////////////////////////////////////////////////////
if (currentTime - timeStamp < debounceDelay) {
return;
}
if (currentTime - timeStamp >= debounceDelay) {
newSpoolPinReading = digitalRead(phoneSpoolPin);
digitalWrite(phoneSpoolLED, !newSpoolPinReading);
newPulsePinReading = digitalRead(phonePulsePin);
digitalWrite(phonePulseLED, !newPulsePinReading);
if (newPulsePinReading != oldPulsePinReading) {
if (newPulsePinReading == HIGH) {
pulsesCounted++;
}
currentTime = millis(); // for end-of-dial timer upgrade
timeStamp = currentTime;
oldPulsePinReading = newPulsePinReading;
}
}
if ((currentTime - timeStamp) >= maxPulseInterval && (pulsesCounted > 0)) {
if (currentDigitCount < telNumLength) {
if (pulsesCounted == 10) {
pulsesCounted = 0;
}
telNumArray[currentDigitCount] = pulsesCounted | '0';
currentDigitCount++;
telNumArray[currentDigitCount] = 0;
}
if (currentDigitCount == telNumLength) {
nextPhoneState = CONNECTED;
}
pulsesCounted = 0;
}
// add end-of-dial timer upgrade
}
void phone_State_CONNECTED() {
///////////////////////////////////////////////////////////////////////////////////////////////////////
if (lastPhoneState != nextPhoneState) { // set new timestamp when newly entering CONNECTED state //
phone_Set_State_LED_Indicators(0); // //
digitalWrite(phoneState_CONNECTED_LED, 1); // //
//currentTime = millis(); // use timeStamp from switch_Phone_States() // //
timeStamp = currentTime; // //
//noActivityStartTime = currentTime; // //
lastPhoneState = nextPhoneState; // //
return; // //
} // //
///////////////////////////////////////////////////////////////////////////////////////////////////////
if (strcmp(telNumArray, "0") == 0) {
phone_Tel_Num_0 (); //nextBeaconMode = OFF
phone_Set_State_LED_Indicators(1);
}
if (strcmp(telNumArray, "1") == 0) {
phone_Tel_Num_1 (); // nextBeaconMode = ON
}
if (strcmp(telNumArray, "2") == 0) {
phone_Tel_Num_2 (); // nextBeaconMode = FLASH
}
if (strcmp(telNumArray, "3") == 0) {
phone_Tel_Num_3 (); // nextBeaconMode = PULSE
}
if (strcmp(telNumArray, "4") == 0) {
phone_Tel_Num_4 (); // beaconNextColour = WHITE
}
if (strcmp(telNumArray, "5") == 0) {
phone_Tel_Num_5 (); // beaconNextColour = BLUE
}
else {
//
}
}
void phone_Set_State_LED_Indicators(byte brightness) {
digitalWrite(phoneState_HOOK_READY_LED, brightness);
digitalWrite(phoneState_DIAL_TONE_LED, brightness);
digitalWrite(phoneState_DIALS_NUM_LED, brightness);
digitalWrite(phoneState_CONNECTED_LED, brightness);
}
void switch_Phone_States() {
currentTime = millis();
if (lastPhoneState != nextPhoneState) { // set new timestamp only when phoneState changes
timeStamp = currentTime;
phone_Set_State_LED_Indicators(0);
lastPhoneState = nextPhoneState;
}
switch (nextPhoneState) {
case HOOK_RESET:
phone_State_HOOK_RESET();
break;
case HOOK_READY:
phone_State_HOOK_READY();
break;
case DIAL_TONE:
phone_State_DIAL_TONE();
break;
case DIALS_NUM:
phone_State_DIALS_NUM();
break;
case CONNECTED:
phone_State_CONNECTED();
break;
}
}
// --- Functions --- Beacon ---
void beacon_Setup() {
pinMode(beaconBluePin, OUTPUT);
pinMode(beaconAmberPin, OUTPUT);
pinMode(beaconWhitePin, OUTPUT);
beaconNextColour = WHITE;
nextBeaconMode = ON;
beaconSwitchColour();
beacon_Switch_Modes();
}
void beacon_Pulse_Timing(byte beaconIntervalDuration) {
// future upgrade to use generic timer function, instead of this dedicated timer
beaconCurrentTime = millis();
if (beaconCurrentTime - beaconTimeStamp < beaconIntervalDuration) {
return;
}
if (beaconCurrentTime - beaconTimeStamp >= beaconIntervalDuration) {
beaconLoopCount++;
beaconTimeStamp = beaconCurrentTime;
}
}
void beaconSwitchColour() {
beacon_Mode_ON_or_OFF(LOW);
switch (beaconNextColour) {
case WHITE:
beaconOutputPin = beaconWhitePin;
break;
case BLUE:
beaconOutputPin = beaconBluePin;
break;
default:
beaconOutputPin = beaconWhitePin;
break;
}
beacon_Mode_ON_or_OFF(beaconBrightness);
}
void beacon_Switch_Modes() { // works as intended
currentTime = millis();
switch (nextBeaconMode) {
case ON:
beacon_Mode_ON_or_OFF(HIGH);
break;
case OFF:
beacon_Mode_ON_or_OFF(LOW);
break;
case PULSE:
beacon_Mode_PULSE(beaconOutputPin, beaconPulseCycleLength);
break;
case FLASH:
beacon_Mode_FLASH();
break;
default:
beacon_Mode_ON_or_OFF(HIGH);
break;
}
}
void beacon_Mode_ON_or_OFF(int beaconBrightness) {
digitalWrite(beaconOutputPin, beaconBrightness);
}
void beacon_Mode_PULSE(byte beaconOutputPin, int beaconPulseInterval) { // works as intended
beacon_Pulse_Timing(beaconPulseInterval);
float radian = DEG_TO_RAD * beaconLoopCount;
int sinOut = constrain ((sin (radian) * 128) + 128, 0, 255);
analogWrite(beaconOutputPin, sinOut);
if (beaconLoopCount == 360) {
beaconLoopCount = 0;
}
}
void beacon_Mode_FLASH() { // beacon flash AMBER with "chirp" sound fx
// TBD
return;
}
// --- The Big Picture ---
void phone_Tel_Num_0 () { // turn beacon OFF
nextBeaconMode = OFF;
}
void phone_Tel_Num_1 () { // turn beacon ON
nextBeaconMode = ON;
}
void phone_Tel_Num_2 () {
nextBeaconMode = FLASH;
}
void phone_Tel_Num_3 () { // beacon fade-up / fade-down
nextBeaconMode = PULSE;
}
void phone_Tel_Num_4 () { // change beacon colour to WHITE
digitalWrite(beaconOutputPin, LOW); // turn OFF previous beacon colour
beaconOutputPin = beaconWhitePin;
}
void phone_Tel_Num_5 () { // change beacon colour to BLUE
digitalWrite(beaconOutputPin, LOW);
beaconOutputPin = beaconBluePin;
}
void phone_Tel_Num_6 () { // dummy/placeholder
return;
}
void phone_Tel_Num_7 () { // dummy/placeholder
return;
}
void setup() {
phone_Setup();
beacon_Setup();
}
void loop() {
beacon_Switch_Modes();
phone_Hook_Pin_State();
switch_Phone_States();
}
//
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
LED01:A
LED01:C
LED02:A
LED02:C
LED03:A
LED03:C
LED04:A
LED04:C
LED05:A
LED05:C
LED06:A
LED06:C
LED07:A
LED07:C
LED08:A
LED08:C
LED09:A
LED09:C
LED10:A
LED10:C
LED11:A
LED11:C
dial:GND
dial:DIAL
dial:PULSE
hook:1
hook:2
hook:3