#include <LedControl.h>
#include <EEPROM.h>
#define BUTTON_PIN 2
#define ANALOG_PIN A0
#define BUZZER_PIN 8
/* MAX7219: first the pins used are defined
DIN pin 12
CLK pin 11
CS pin 10
and then the number of displays = 3
*/
LedControl lc = LedControl(12, 11, 10, 3);
int minVal = 0;
int maxVal = 1023;
int angle = 0;
volatile bool buttonPressed = false; // Set in ISR when button pressed
volatile unsigned long lastInterruptTime = 0;
bool longPressDetected = false;
bool lastWasError = false;
enum CalibState { NONE, WAIT_MIN, WAIT_MAX };
CalibState calibState = NONE;
unsigned long lastSwitch = 0;
bool showCompass = false;
// Buzzer logic
bool buzzerActive = false;
unsigned long buzzerStartTime = 0;
unsigned long lastBuzzerTime = 0;
const unsigned long buzzerDuration = 500; // half a second
const unsigned long buzzerCooldown = 5000; // time before it sounds again (5sec)
void handleButtonInterrupt() {
unsigned long interruptTime = millis();
// Debounce: ignore if interrupt happened less than 50ms ago
if (interruptTime - lastInterruptTime > 50) {
buttonPressed = true;
lastInterruptTime = interruptTime;
}
}
void setup() {
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
for (int i = 0; i < 3; i++) {
lc.shutdown(i, false); // The MAX7219 devices wake up in power-saving mode
lc.clearDisplay(i);
lc.setIntensity(i, 8); // Set the brightness to a medium value
}
showText("ok");
delay(1000);
// Attach interrupt on FALLING edge (button press)
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), handleButtonInterrupt, FALLING);
if (digitalRead(BUTTON_PIN) == LOW) {
unsigned long buttonDownTime = millis();
while (digitalRead(BUTTON_PIN) == LOW) {
if (millis() - buttonDownTime > 3000) {
longPressDetected = true;
break;
}
}
if (longPressDetected) {
resetCalibration();
} else {
runCalibration();
}
} else {
EEPROM.get(0, minVal);
EEPROM.get(2, maxVal);
if (minVal >= maxVal || abs(maxVal - minVal) < 10) {
minVal = 0;
maxVal = 1023;
}
}
Serial.println("[0] System initialized.");
} // end of setup
void loop() {
unsigned long now = millis();
int input = analogRead(ANALOG_PIN); // get the heading from the rotator controller
// we then scale it to get the DC value converted into a compass bearing 0 to 360
if (abs(maxVal - minVal) < 5) {
angle = 0;
} else {
input = constrain(input, minVal, maxVal);// Do we need the constrain here? We know the value is never going to go to high. So we should only need the map line. But for safety we can keep it.
// angle = map(input, minVal, maxVal, 0, 360);
// angle = constrain(angle, 0, 360);
angle = map(input, minVal, maxVal, 0, 240); // This is the IMPORTANT line
angle = constrain(angle, 0, 240); //Ditto we should not need to constrain here either. Again keep for safety reasons.
}
// Logging the output to the serial line (over USB)
Serial.print("[");
Serial.print(now); // now is the current time in millis since boot, probably not too useful
Serial.print("] Analog: ");
Serial.print(input);
Serial.print(" | Angle: ");
Serial.print(angle);
Serial.println();
if (angle >= 240) {
Serial.print(" --> ERR (angle >= 240)");
showError();
if (!lastWasError && (now - lastBuzzerTime >= buzzerCooldown)) {
buzzError(now);
}
lastWasError = true;
} else { // now we either show compass ordinals or the bearing, alternating every 2 seconds
if (now - lastSwitch > 2000) {
showCompass = !showCompass;
lastSwitch = now;
}
if (showCompass) {
displayCompass(angle);
Serial.println(" | Display: Compass");
} else {
displayNumericAngle(angle);
Serial.println(" | Display: Angle");
}
lastWasError = false;
}
updateBuzzer(now);
// Handle button press event detected in ISR
if (buttonPressed) {
buttonPressed = false; // Reset flag
if (calibState != NONE) {
// Handle calibration steps based on button press
if (calibState == WAIT_MIN) {
minVal = analogRead(ANALOG_PIN);
showText("SET"); // prompt to show we have saved the reading
delay(1000);
showText("MAX"); // prompt that we are now going to set maximum
calibState = WAIT_MAX;
} else if (calibState == WAIT_MAX) {
maxVal = analogRead(ANALOG_PIN);
if (maxVal < minVal + 10) {
maxVal = minVal + 10;
}
EEPROM.put(0, minVal);
EEPROM.put(2, maxVal);
showText("OK "); // prompt that the calibration is complete
delay(1000);
calibState = NONE;
}
}
}
// Small delay to avoid flooding serial output
delay(100);
} // end of main loop
// ========== Buzzer Logic ==========
void buzzError(unsigned long now) {
tone(BUZZER_PIN, 440);
buzzerStartTime = now;
lastBuzzerTime = now;
buzzerActive = true;
Serial.print(" [");
// Serial.print(now);
Serial.println("] Buzzer ON");
} // end of buzzError
void updateBuzzer(unsigned long now) {
if (buzzerActive && (now - buzzerStartTime >= buzzerDuration)) {
noTone(BUZZER_PIN);
buzzerActive = false;
Serial.print("[");
// Serial.print(now);
Serial.println("] Buzzer OFF");
}
} // end of updateBuzzer
// ========== Calibration ==========
void runCalibration() {
calibState = WAIT_MIN;
showText("CAL"); // prompt we are in calibrate routine
delay(1000);
showText("MIN"); // prompt to set minimum
// rotate the antenna to minimum and then press the button
// Wait for button presses handled by interrupt + main loop logic
while (calibState != NONE) {
// Main loop will handle button presses via interrupt flag
// Just idle here (blocking)
delay(10);
}
} /// end of runCalibration
void resetCalibration() {
minVal = 0;
maxVal = 1023;
EEPROM.put(0, minVal);
EEPROM.put(2, maxVal);
showText("RST");
delay(1000);
} // end of resetCalibration
// ========== Display Functions ==========
void displayNumericAngle(int val) {
char buf[4];
snprintf(buf, sizeof(buf), "%3d", val);
bool warning = (val >= 240);
for (int i = 0; i < 3; i++) {
lc.clearDisplay(i);
char c = buf[2 - i];
if (c != ' ') {
lc.setChar(i, 0, c, warning);
}
}
} // end of displayNumericAngle
void displayCompass(int val) {
const char* directions[] = {
"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
"S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"
};
int index = (int)((val + 11.25) / 22.5) % 16;
const char* dir = directions[index];
for (int i = 0; i < 3; i++) {
lc.clearDisplay(i);
char c = (i < strlen(dir)) ? dir[2 - i] : ' ';
if (c != ' ') {
lc.setChar(i, 0, c, false);
}
Serial.println(dir);
}
} // end of displayCompass
void showText(const char* txt) {
for (int i = 0; i < 3; i++) {
lc.clearDisplay(i);
char c = txt[2 - i];
lc.setChar(i, 0, c, false);
}
} // end of showText
void showError() {
showText("ERR");
} // end of showError
// ============== EOF ==============