dac-usb-bluetooth/control-panel-for-for-dac/control-panel-for-for-dac.ino

410 lines
14 KiB
Arduino
Raw Permalink Normal View History

// по материалам:
//https://www.instructables.com/Simple-LCD-MENU-Using-Arduino/
// https://alexgyver.ru/lessons/
// https://www.hackster.io/mircemk/diy-arduino-vfd-display-20x2-vu-volume-unit-meter-37898f
// https://github.com/AlexGyver/FHTSpectrumAnalyzer/blob/master/Firmware/spertrum1602/spertrum1602.ino
#define GAIN 5 // усиление 0...50
#define STEP 20 // плавность полос 0...20
#define RL 1 // RL - горизонт, вертикаль 0...1
// Выходы для управления реле (вкл.вкл ЦАП/Блютуз)
#define RELAY_POWER_USB 7
#define RELAY_POWER_BT 6
#define RELAY_POWER_MP3 10
#define RELAY_OUT_BT_LEFT 8
#define RELAY_OUT_BT_RIGHT 9
#define RELAY_OUT_MP3_LEFT 11
#define RELAY_OUT_MP3_RIGHT 12
// Кнопки
#define BTN_MENU 2 // Кнопка меню
#define BTN_EXECUTE 3 // Кнопка подтверждения
#define INIT_ADDR 1023 // номер резервной ячейки
#define INIT_KEY 1 // ключ первого запуска
// ---------------- НАСТРОЙКИ ----------------
#define DRIVER_VERSION 0 // 0 - маркировка драйвера кончается на 4АТ, 1 - на 4Т
#define GAIN_CONTROL 0 // ручная настройка потенциометром на громкость (1 - вкл, 0 - выкл)
#define AUTO_GAIN 0 // автонастройка по громкости (экспериментальная функция)
#define VOL_THR 35 // порог тишины (ниже него отображения на матрице не будет)
#define LOW_PASS 30 // нижний порог чувствительности шумов (нет скачков при отсутствии звука)
#define DEF_GAIN 80 // максимальный порог по умолчанию (при GAIN_CONTROL игнорируется)
#define FHT_N 256 // ширина спектра х2
// вручную забитый массив тонов, сначала плавно, потом круче
byte posOffset[16] = {2, 3, 4, 6, 8, 10, 12, 14, 16, 20, 25, 30, 35, 60, 80, 100};
// ---------------- НАСТРОЙКИ ----------------
// ---------------------- ПИНЫ ----------------------
#define AUDIO_IN_L 0 // пин, куда подключен звук
#define AUDIO_IN_R 1 // пин, куда подключен звук
#define POT_PIN 7 // пин потенциометра настройки
// ---------------------- ПИНЫ ----------------------
// --------------- БИБЛИОТЕКИ ---------------
#define LOG_OUT 1
#include <FHT.h> // преобразование Хартли
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define printByte(args) write(args);
double prevVolts = 100.0;
// --------------- БИБЛИОТЕКИ ---------------
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#include <EEPROM.h>
// LiquidCrystal lcdVuMeter(2, 3, 4, 5, 6, 7);// RS,E,D4,D5,D6,D7
LiquidCrystal_I2C lcdVuMeter(0x27, 16, 2);
LiquidCrystal_I2C lcdService(0x26, 16, 2);
bool usb, bluetooth, mp3;
int menu = 1;
bool menuShow = false;
// ------------------------------------- ПОЛОСОЧКИ -------------------------------------
byte v1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111};
byte v2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111};
byte v3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111};
byte v4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111};
byte v5[8] = {0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte v6[8] = {0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte v7[8] = {0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte v8[8] = {0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
// ------------------------------------- ПОЛОСОЧКИ -------------------------------------
byte gain = DEF_GAIN; // усиление по умолчанию
unsigned long gainTimer;
byte maxValue, maxValue_f;
float k = 0.1;
void setup() {
lcdVuMeter.init();
lcdVuMeter.backlight();
lcdVuMeter.clear();
lcdVuMeter.setCursor(0, 0);
lcdVuMeter.print("R");
// lcdVuMeter.createChar(4,znak_r);
lcdVuMeter.setCursor(0, 1);
lcdVuMeter.print("L");
lcdService.init();
lcdService.backlight();
lcdService.clear();
// lcdService.setCursor(0, 0);
// lcdService.print("Menu");
// Кнопка управления (переключения)
pinMode(BTN_MENU, INPUT_PULLUP);
pinMode(BTN_EXECUTE, INPUT_PULLUP);
// Выходы для управления реле (вкл.вкл ЦАП/Блютуз)
pinMode(RELAY_POWER_USB, OUTPUT);
pinMode(RELAY_POWER_BT, OUTPUT);
pinMode(RELAY_POWER_MP3, OUTPUT);
pinMode(RELAY_OUT_BT_LEFT, OUTPUT);
pinMode(RELAY_OUT_BT_RIGHT, OUTPUT);
pinMode(RELAY_OUT_MP3_LEFT, OUTPUT);
pinMode(RELAY_OUT_MP3_RIGHT, OUTPUT);
// lcdVuMeter.createChar(5,znak_l);
// lcdVuMeter.begin(16, 2);// lcdVuMeter 16X2
// analogReference(INTERNAL); // если очень маленький уровень сигнала
pinMode(A0,INPUT);// A0 - аналоговый вход R
pinMode(A1,INPUT);// A1 - аналоговый вход L
// первый запуск и установка занчений переменных в память
if (EEPROM.read(INIT_ADDR) != INIT_KEY) {
EEPROM.write(INIT_ADDR, INIT_KEY);
usb = true;
bluetooth = false;
mp3 = false;
EEPROM.put(10, usb);
EEPROM.put(12, bluetooth);
EEPROM.put(14, mp3);
} else {
usb = EEPROM.read(10);
bluetooth = EEPROM.read(12);
mp3 = EEPROM.read(14);
}
// поднимаем частоту опроса аналогового порта до 38.4 кГц, по теореме
// Котельникова (Найквиста) частота дискретизации будет 19 кГц
// http://yaab-arduino.blogspot.ru/2015/02/fast-sampling-from-analog-input.html
sbi(ADCSRA, ADPS2);
cbi(ADCSRA, ADPS1);
sbi(ADCSRA, ADPS0);
// для увеличения точности уменьшаем опорное напряжение,
// выставив EXTERNAL и подключив Aref к выходу 3.3V на плате через делитель
// GND ---[2х10 кОм] --- REF --- [10 кОм] --- 3V3
analogReference(EXTERNAL);
if (usb) {
digitalWrite(RELAY_POWER_BT,HIGH);
digitalWrite(RELAY_POWER_MP3,HIGH);
digitalWrite(RELAY_POWER_USB,HIGH);
delay(500);
digitalWrite(RELAY_OUT_BT_LEFT,HIGH);
digitalWrite(RELAY_OUT_BT_RIGHT,HIGH);
digitalWrite(RELAY_OUT_MP3_LEFT,HIGH);
digitalWrite(RELAY_OUT_MP3_RIGHT,HIGH);
}
if (bluetooth) {
digitalWrite(RELAY_POWER_USB,HIGH);
digitalWrite(RELAY_POWER_MP3,HIGH);
delay(500);
digitalWrite(RELAY_OUT_MP3_LEFT,HIGH);
digitalWrite(RELAY_OUT_MP3_RIGHT,HIGH);
delay(1000);
digitalWrite(RELAY_OUT_BT_LEFT,LOW);
digitalWrite(RELAY_OUT_BT_RIGHT,LOW);
digitalWrite(RELAY_POWER_BT,LOW);
}
if (mp3) {
}
lcdChars(); // подхватить коды полосочек
updateMenu();
}
void lcdChars() {
lcdVuMeter.createChar(0, v1);
lcdVuMeter.createChar(1, v2);
lcdVuMeter.createChar(2, v3);
lcdVuMeter.createChar(3, v4);
lcdVuMeter.createChar(4, v5);
lcdVuMeter.createChar(5, v6);
lcdVuMeter.createChar(6, v7);
lcdVuMeter.createChar(7, v8);
}
String setUSBinput() {
return "Input: USB";
}
void updateMenu() {
switch (menu) {
case 0:
menu = 1;
break;
case 1:
lcdService.clear();
lcdService.print(">USB DAC");
lcdService.setCursor(2, 1);
if(usb){
lcdService.print("On");
}else{
lcdService.print("Off");
}
break;
case 2:
lcdService.clear();
lcdService.print(">Bluetooth");
lcdService.setCursor(2, 1);
if(bluetooth){
lcdService.print("On");
}else{
lcdService.print("Off");
}
break;
case 3:
lcdService.clear();
lcdService.print(">MP3 module");
lcdService.setCursor(2, 1);
if(mp3){
lcdService.print("On");
}else{
lcdService.print("Off");
}
break;
case 4:
menu = 0;
menuShow = false;
lcdService.clear();
break;
}
}
void executeMenuAction() {
switch (menu) {
case 1:
actionUSB();
break;
case 2:
actionBT();
break;
case 3:
actionMP3();
break;
}
EEPROM.put(10, usb);
EEPROM.put(12, bluetooth);
EEPROM.put(14, mp3);
}
void actionUSB() {
// lcdService.clear();
lcdService.setCursor(2,1);
lcdService.print("On ");
digitalWrite(RELAY_POWER_BT,HIGH);
digitalWrite(RELAY_POWER_MP3,HIGH);
digitalWrite(RELAY_POWER_USB,HIGH);
delay(500);
digitalWrite(RELAY_OUT_BT_LEFT,HIGH);
digitalWrite(RELAY_OUT_BT_RIGHT,HIGH);
digitalWrite(RELAY_OUT_MP3_LEFT,HIGH);
digitalWrite(RELAY_OUT_MP3_RIGHT,HIGH);
usb = true;
bluetooth = false;
mp3 = false;
// delay(1500);
}
void actionBT() {
// lcdService.clear();
lcdService.setCursor(2,1);
lcdService.print("On ");
digitalWrite(RELAY_POWER_USB,HIGH);
digitalWrite(RELAY_POWER_MP3,HIGH);
delay(500);
digitalWrite(RELAY_OUT_MP3_LEFT,HIGH);
digitalWrite(RELAY_OUT_MP3_RIGHT,HIGH);
delay(1000);
digitalWrite(RELAY_OUT_BT_LEFT,LOW);
digitalWrite(RELAY_OUT_BT_RIGHT,LOW);
digitalWrite(RELAY_POWER_BT,LOW);
usb = false;
bluetooth = true;
mp3 = false;
// delay(1500);
}
void actionMP3() {
// lcdService.clear();
lcdService.setCursor(2,1);
lcdService.print("On ");
digitalWrite(RELAY_POWER_USB,HIGH);
digitalWrite(RELAY_POWER_BT,HIGH);
delay(500);
digitalWrite(RELAY_OUT_BT_LEFT,HIGH);
digitalWrite(RELAY_OUT_BT_RIGHT,HIGH);
delay(1000);
digitalWrite(RELAY_OUT_MP3_LEFT,LOW);
digitalWrite(RELAY_OUT_MP3_RIGHT,LOW);
digitalWrite(RELAY_POWER_MP3,LOW);
usb = false;
bluetooth = false;
mp3 = true;
delay(1500);
}
uint32_t btnTimer = 0;
void loop() {
lcdService.setCursor(0,0);
// Выводим на экран информацию какой модуль включен
if(!menuShow){
if(usb){
lcdService.print("USB DAC");
lcdService.setCursor(0,1);
lcdService.print(setUSBinput());
// actionUSB();
}
if(bluetooth){
lcdService.print("Bluetooth");
// actionBT();
}
if(mp3){
lcdService.print("MP3 module");
// actionMP3();
}
}
// bool btnMenuState = digitalRead(BTN_MENU);
// bool btnExecuteState = digitalRead(BTN_EXECUTE);
if (!digitalRead(BTN_MENU)){
menuShow = true;
menu ++;
updateMenu();
delay(100);
while (!digitalRead(BTN_MENU));
}
if (!digitalRead(BTN_EXECUTE)){
executeMenuAction();
updateMenu();
delay(100);
while (!digitalRead(BTN_EXECUTE));
}
// Тут будет код обработки данных от USB DAC для индикации режима его работы
// if (digitalRead(8) && usb) {
// lcdService.setCursor(0,1);
// lcdService.print("Coaxial");
// } else if (digitalRead(9) && usb) {
// lcdService.setCursor(0,1);
// lcdService.print("Optical");
// } else if (digitalRead(10) && usb) {
// lcdService.setCursor(0,1);
// lcdService.print("Audio ");
// } else {
// lcdService.setCursor(0,1);
// lcdService.print(" ");
// }
// lcdService.setCursor(11,1);
// lcdService.print(digitalRead(8), digitalRead(9));
if (GAIN_CONTROL) gain = map(analogRead(POT_PIN), 0, 1023, 0, 150);
analyzeAudio(); // функция FHT, забивает массив fht_log_out[] величинами по спектру
for (int pos = 0; pos < 16; pos++) { // для окошек дисплея с 0 по 15
// найти максимум из пачки тонов
if (fht_log_out[posOffset[pos]] > maxValue) maxValue = fht_log_out[posOffset[pos]];
lcdVuMeter.setCursor(pos, 0);
// преобразовать значение величины спектра в диапазон 0..15 с учётом настроек
int posLevel = map(fht_log_out[posOffset[pos]], LOW_PASS, gain, 0, 15);
posLevel = constrain(posLevel, 0, 15);
if (posLevel > 7) { // если значение больше 7 (значит нижний квадратик будет полный)
lcdVuMeter.printByte(posLevel - 8); // верхний квадратик залить тем что осталось
lcdVuMeter.setCursor(pos, 1); // перейти на нижний квадратик
lcdVuMeter.printByte(7); // залить его полностью
} else { // если значение меньше 8
lcdVuMeter.print(" "); // верхний квадратик пустой
lcdVuMeter.setCursor(pos, 1); // нижний квадратик
lcdVuMeter.printByte(posLevel); // залить полосками
}
}
if (AUTO_GAIN) {
maxValue_f = maxValue * k + maxValue_f * (1 - k);
if (millis() - gainTimer > 1500) { // каждые 1500 мс
// если максимальное значение больше порога, взять его как максимум для отображения
if (maxValue_f > VOL_THR) gain = maxValue_f;
// если нет, то взять порог побольше, чтобы шумы вообще не проходили
else gain = 100;
gainTimer = millis();
}
}
}
void analyzeAudio() {
for (int i = 0 ; i < FHT_N ; i++) {
int sample = (analogRead(AUDIO_IN_L) + analogRead(AUDIO_IN_R)) / 2;
fht_input[i] = sample; // put real data into bins
}
fht_window(); // window the data for better frequency response
fht_reorder(); // reorder the data before doing the fht
fht_run(); // process the data in the fht
fht_mag_log(); // take the output of the fht
}