#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// OLED налаштування
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Піни джойстика
#define JOY_HORZ 35
#define JOY_VERT 34
#define JOY_SEL 32
// Пін п'єзопіщалки
#define BUZZER 25
// Ноти (частоти в Гц)
#define NOTE_C4 262
#define NOTE_D4 294
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_G4 392
#define NOTE_A4 440
#define NOTE_B4 494
#define NOTE_C5 523
// Масив нот
int notes[] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5};
String noteNames[] = {"C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5"};
int currentNote = 0; // Поточна вибрана нота
// Змінні для джойстика
int lastX = 0;
int lastY = 0;
unsigned long lastMoveTime = 0;
unsigned long lastButtonTime = 0;
const int moveDelay = 200; // Затримка між зміною нот (мс)
const int debounceDelay = 50;
// Режими
bool isPlaying = true; // true - граємо, false - тихо (тільки візуалізація)
void setup() {
Serial.begin(115200);
// Налаштування пінів
pinMode(JOY_SEL, INPUT_PULLUP);
pinMode(BUZZER, OUTPUT);
// Ініціалізація джойстика
lastX = analogRead(JOY_HORZ);
lastY = analogRead(JOY_VERT);
// Ініціалізація OLED
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 не знайдено!");
for(;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// Привітання
showWelcome();
delay(2000);
// Показати початковий екран
drawMusicInterface();
}
void loop() {
// Читання джойстика
int xVal = analogRead(JOY_HORZ);
int yVal = analogRead(JOY_VERT);
// Горизонтальний рух - вибір ноти
if(millis() - lastMoveTime > moveDelay) {
if(xVal > 3000) { // Вправо
currentNote = min(currentNote + 1, 7);
lastMoveTime = millis();
drawMusicInterface();
playCurrentNote();
}
else if(xVal < 1000) { // Вліво
currentNote = max(currentNote - 1, 0);
lastMoveTime = millis();
drawMusicInterface();
playCurrentNote();
}
}
// Вертикальний рух - зміна октави або гучності
if(millis() - lastMoveTime > moveDelay) {
if(yVal > 3000) { // Вниз
// Зменшуємо "гучність" (тривалість звуку)
lastMoveTime = millis();
drawMusicInterface();
if(isPlaying) playCurrentNote();
}
else if(yVal < 1000) { // Вверх
// Збільшуємо "гучність"
lastMoveTime = millis();
drawMusicInterface();
if(isPlaying) playCurrentNote();
}
}
// Кнопка - ввімкнути/вимкнути звук
if(digitalRead(JOY_SEL) == LOW) {
if(millis() - lastButtonTime > debounceDelay) {
lastButtonTime = millis();
isPlaying = !isPlaying;
drawMusicInterface();
if(!isPlaying) {
noTone(BUZZER); // Вимикаємо звук
} else {
playCurrentNote(); // Граємо ноту при ввімкненні
}
delay(200); // Захист від повтору
}
}
delay(20);
}
// Функція для відтворення ноти
void playCurrentNote() {
if(isPlaying) {
tone(BUZZER, notes[currentNote], 300); // Граємо 300 мс
drawNoteWaveform(); // Анімація звукової хвилі
}
}
// Головний інтерфейс
void drawMusicInterface() {
display.clearDisplay();
// Рамка
display.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
// Назва інструменту
display.setTextSize(1);
display.setCursor(5, 3);
display.print("MUSIC SYNTH");
// Поточна нота
display.setTextSize(2);
display.setCursor(30, 20);
display.print(noteNames[currentNote]);
// Частота
display.setTextSize(1);
display.setCursor(30, 40);
display.print(notes[currentNote]);
display.print(" Hz");
// Візуалізація вибраної ноти (клавіші)
drawKeyboard();
// Режим (звук ввімк/вимк)
display.setCursor(5, 55);
if(isPlaying) {
display.print("SOUND: ON ");
display.fillRect(70, 54, 8, 8, SSD1306_WHITE); // Іконка динаміка
} else {
display.print("SOUND: OFF");
}
// Інструкція
display.setTextSize(0); // Найменший розмір (6x8 пікселів)
display.setCursor(5, SCREEN_HEIGHT - 7);
display.print("L/R:Note SEL:ON/OFF");
display.display();
}
// Малювання клавіатури піаніно
void drawKeyboard() {
int keyWidth = 12;
int startX = 25;
int y = 46;
for(int i = 0; i < 8; i++) {
int x = startX + i * keyWidth;
if(i == currentNote) {
display.fillRect(x, y, keyWidth - 1, 8, SSD1306_WHITE); // Активна клавіша
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
display.setCursor(x + 2, y + 1);
display.print(i);
display.setTextColor(SSD1306_WHITE);
} else {
display.drawRect(x, y, keyWidth - 1, 8, SSD1306_WHITE); // Звичайна клавіша
display.setCursor(x + 2, y + 1);
display.print(i);
}
}
}
// Проста візуалізація звукової хвилі
void drawNoteWaveform() {
for(int i = 0; i < 3; i++) {
// Малюємо хвилю біля ноти
display.drawLine(90 + i*2, 25 - i*2, 95 + i*2, 25 + i*2, SSD1306_WHITE);
display.drawLine(90 + i*2, 25 + i*2, 95 + i*2, 25 - i*2, SSD1306_WHITE);
display.display();
delay(30);
}
drawMusicInterface(); // Оновлюємо екран
}
// Привітання
void showWelcome() {
display.clearDisplay();
display.setTextSize(2);
display.setCursor(15, 15);
display.println("MUSIC");
display.setCursor(20, 35);
display.println("SYNTH");
display.setTextSize(1);
display.setCursor(20, 55);
display.println("by B.Vorobiei");
display.display();
delay(2000);
// Анімація завантаження
display.clearDisplay();
display.setCursor(20, 30);
display.print("Loading...");
for(int i = 0; i < 10; i++) {
display.fillRect(40 + i*4, 40, 2, 5, SSD1306_WHITE);
display.display();
delay(50);
}
delay(500);
}