#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Analog pins for simulated mics (pots)
#define MIC_FRONT A0
#define MIC_RIGHT A1
#define MIC_BACK A2
#define MIC_LEFT A3
// RGB LED pins (common cathode)
#define RED_PIN 6
#define GREEN_PIN 7
#define BLUE_PIN 8
// Buzzer
#define BUZZER_PIN 9
// Threshold for “No Sound” region (tune as needed)
int quietThreshold = 50;
// Simple smoothing
const int AVG_N = 5;
int bufF[AVG_N], bufR[AVG_N], bufB[AVG_N], bufL[AVG_N];
int idx = 0;
void setColor(int r, int g, int b) {
analogWrite(RED_PIN, r);
analogWrite(GREEN_PIN, g);
analogWrite(BLUE_PIN, b);
}
void buzzOnce(int dur) {
digitalWrite(BUZZER_PIN, HIGH);
delay(dur);
digitalWrite(BUZZER_PIN, LOW);
}
void setup() {
Serial.begin(9600);
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
setColor(0,0,0);
digitalWrite(BUZZER_PIN, LOW);
// OLED init (0x3C typical)
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
// If OLED fails, continue headless
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.println("Sound Direction");
display.setCursor(0, 12);
display.println("Quad-Mic (sim via POT)");
display.display();
// init smoothing buffers
for (int i=0;i<AVG_N;i++){ bufF[i]=bufR[i]=bufB[i]=bufL[i]=0; }
}
void loop() {
// Read raw
int rawF = analogRead(MIC_FRONT);
int rawR = analogRead(MIC_RIGHT);
int rawB = analogRead(MIC_BACK);
int rawL = analogRead(MIC_LEFT);
// Update smoothing buffers
bufF[idx]=rawF; bufR[idx]=rawR; bufB[idx]=rawB; bufL[idx]=rawL;
idx = (idx + 1) % AVG_N;
// Compute moving averages
int sF=0, sR=0, sB=0, sL=0;
for (int i=0;i<AVG_N;i++){ sF+=bufF[i]; sR+=bufR[i]; sB+=bufB[i]; sL+=bufL[i]; }
sF/=AVG_N; sR/=AVG_N; sB/=AVG_N; sL/=AVG_N;
// Decide direction
int maxVal = max(max(sF, sR), max(sB, sL));
String dir = "No Sound";
if (maxVal < quietThreshold) {
dir = "No Sound";
} else if (maxVal == sF) {
dir = "Front";
} else if (maxVal == sR) {
dir = "Right";
} else if (maxVal == sB) {
dir = "Back";
} else if (maxVal == sL) {
dir = "Left";
}
// OLED update
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0); display.println("Sound Direction:");
display.setTextSize(2);
display.setCursor(10, 24); display.println(dir);
display.setTextSize(1);
display.setCursor(0, 50);
display.print("F:"); display.print(sF);
display.print(" R:"); display.print(sR);
display.print(" B:"); display.print(sB);
display.print(" L:"); display.print(sL);
display.display();
// RGB LED color by direction (CC LED: higher value = brighter)
if (dir == "Front") setColor(255, 0, 0 ); // Red
else if (dir == "Right") setColor(0, 0, 255 ); // Blue
else if (dir == "Back") setColor(0, 255, 0 ); // Green
else if (dir == "Left") setColor(255, 255, 0 ); // Yellow
else setColor(0, 0, 0 ); // Off
// Buzzer (short beep on any valid direction)
static String lastDir = "No Sound";
if (dir != "No Sound" && dir != lastDir) {
buzzOnce(80);
}
lastDir = dir;
// Serial output (friendly for Serial Plotter)
// Use "name:value" pairs separated by spaces, same order each line
Serial.print("Front:"); Serial.print(sF);
Serial.print(" Right:"); Serial.print(sR);
Serial.print(" Back:"); Serial.print(sB);
Serial.print(" Left:"); Serial.print(sL);
Serial.print(" Max:"); Serial.print(maxVal);
Serial.print(" Dir:");
// encode direction as a numeric channel for plotting (optional):
// No=0, F=1, R=2, B=3, L=4
int dirCode = 0;
if (dir=="Front") dirCode=1;
else if (dir=="Right") dirCode=2;
else if (dir=="Back") dirCode=3;
else if (dir=="Left") dirCode=4;
Serial.println(dirCode);
delay(80); // ~12 Hz refresh
}