Hi all, Im brand new to coding and have been working on this sketch for a Teensy 4.1, a ILI9341 TFT screen, and rotary encoder. It seems to work ok, but I would love someone (who knows what they're doing) to look at it before I add more functions. The audio filtering seems to operate as expected. The 7 variables are saved to eeprom and are recalled when the Teensy is rebooted. The screen turns off after a short delay to reduce burn-in and is turned back on when the encoder is turned. Any suggestions would be greatly appreciated!
#include "OpenAudio_ArduinoLibrary.h"
#include "AudioStream_F32.h"
#include "TD_filterIIR.h"
#include <Audio.h>
#include <ILI9341_t3.h>
#include <Encoder.h>
#include <Bounce.h>
#include <EEPROM.h>
#include "font_Arial.h"
AudioInputI2S_F32 input;
filterIIR hp1;
filterIIR eq1;
AudioOutputI2S_F32 output;
AudioConnection_F32 patchCord1(input, 0, hp1, 0);
AudioConnection_F32 patchCord2(hp1, 0, eq1, 0);
AudioConnection_F32 patchCord3(eq1, 0, output, 0);
AudioConnection_F32 patchCord4(eq1, 0, output, 1);
AudioControlSGTL5000 codec;
#define TFT_DC 9
#define TFT_CS 10
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);
#define ENCODER_A_PIN 5
#define ENCODER_B_PIN 6
#define ENCODER_BTN_PIN A0
Encoder encoder(ENCODER_A_PIN, ENCODER_B_PIN);
Bounce encoderButton = Bounce(ENCODER_BTN_PIN, 10);
uint32_t lastEncoderReadTime = 0;
elapsedMillis timeSinceLastPress;
int32_t oldEncoderPosition = -999;
unsigned long lastInteractionTime = 0;
const unsigned long screenTimeout = 30000; // 30 seconds
const int backlightPin = A1; // Pin 7 casued screen to flicker
void turnScreenOn() {
digitalWrite(backlightPin, HIGH); // Turns the backlight on
tft.setRotation(3); // Ensure the orientation is set correctly after turning on
}
void turnScreenOff() {
digitalWrite(backlightPin, LOW); // Turns the backlight off
}
// EEPROM addresses for each setting
const int EEPROM_ADDR_HP1 = 0;
const int EEPROM_ADDR_EQ1 = EEPROM_ADDR_HP1 + sizeof(float); // Start after HP1
const int EEPROM_ADDR_EQ1_Q = EEPROM_ADDR_EQ1 + sizeof(float);
const int EEPROM_ADDR_EQ1_GAIN = EEPROM_ADDR_EQ1_Q + sizeof(float);
const int EEPROM_ADDR_EQ2 = EEPROM_ADDR_EQ1_GAIN + sizeof(float);
const int EEPROM_ADDR_EQ2_Q = EEPROM_ADDR_EQ2 + sizeof(float);
const int EEPROM_ADDR_EQ2_GAIN = EEPROM_ADDR_EQ2_Q + sizeof(float);
float hp1_lowcut = 120;
float eq1_lowf = 277;
float eq1_lowq = 2.0;
float eq1_lowgain = -8.0;
float eq2_lowf = 2750;
float eq2_lowq = 5.5;
float eq2_lowgain = -4.0;
void saveSettingsToEEPROM() {
EEPROM.put(EEPROM_ADDR_HP1, hp1_lowcut);
EEPROM.put(EEPROM_ADDR_EQ1, eq1_lowf);
EEPROM.put(EEPROM_ADDR_EQ1_Q, eq1_lowq);
EEPROM.put(EEPROM_ADDR_EQ1_GAIN, eq1_lowgain);
EEPROM.put(EEPROM_ADDR_EQ2, eq2_lowf);
EEPROM.put(EEPROM_ADDR_EQ2_Q, eq2_lowq);
EEPROM.put(EEPROM_ADDR_EQ2_GAIN, eq2_lowgain);
}
int menuSelected = 0;
int oldMenuSelected = -1;
bool isValueSelected = false;
String menuItems[] = {"Low cut", "EQ1 Freq", "EQ1 Q", "EQ1 Gain", "EQ2 Freq", "EQ2 Q", "EQ2 Gain"};
void loadSettingsFromEEPROM() {
EEPROM.get(EEPROM_ADDR_HP1, hp1_lowcut);
EEPROM.get(EEPROM_ADDR_EQ1, eq1_lowf);
EEPROM.get(EEPROM_ADDR_EQ1_Q, eq1_lowq);
EEPROM.get(EEPROM_ADDR_EQ1_GAIN, eq1_lowgain);
EEPROM.get(EEPROM_ADDR_EQ2, eq2_lowf);
EEPROM.get(EEPROM_ADDR_EQ2_Q, eq2_lowq);
EEPROM.get(EEPROM_ADDR_EQ2_GAIN, eq2_lowgain);
// Apply the loaded settings to the filters
hp1.setBiQuadEq(0,'H',hp1_lowcut,1,0);
eq1.setBiQuadEq(0,'P',eq1_lowf,eq1_lowq,eq1_lowgain);
eq1.setBiQuadEq(1,'P',eq2_lowf,eq2_lowq,eq2_lowgain);
}
void setup() {
AudioMemory(5);
AudioMemory_F32(12);
codec.enable();
codec.inputSelect(AUDIO_INPUT_LINEIN);
codec.adcHighPassFilterDisable();
codec.lineInLevel(2);
codec.volume(0.7);
tft.begin();
tft.setRotation(3); // Set the rotation before turning the screen on
tft.fillScreen(ILI9341_BLACK);
tft.setFont(Arial_14);
pinMode(backlightPin, OUTPUT);
turnScreenOff(); // Start with the screen off
delay(100);
tft.begin();
turnScreenOn(); // Turn the screen on after initialization
lastInteractionTime = millis(); // Reset the interaction timer
loadSettingsFromEEPROM();
}
void loop() {
handleEncoder();
handleEncoderButton();
checkScreenTimeout();
}
void handleEncoder() {
int32_t newPos = encoder.read() / 4; // Dividing by the encoder resolution
if (newPos != oldEncoderPosition) {
lastInteractionTime = millis(); // Update last interaction time on any encoder move
turnScreenOn(); // Ensure the screen is on when the encoder is moved
int acceleration = 1;
// Determine the acceleration based on the difference
int diff = abs(newPos - oldEncoderPosition);
if (diff > 2) acceleration = 3; // Adjust this as per your needs
if (isValueSelected) {
adjustValue((newPos > oldEncoderPosition ? 1 : -1) * acceleration);
} else {
if (newPos > oldEncoderPosition) {
menuSelected = (menuSelected + 1) % 7;
} else {
menuSelected = (menuSelected - 1 + 7) % 7;
}
drawMenu();
}
oldEncoderPosition = newPos;
}
}
void checkScreenTimeout() {
if (millis() - lastInteractionTime > screenTimeout) {
turnScreenOff();
}
}
void handleEncoderButton() {
encoderButton.update();
if (encoderButton.fallingEdge() && timeSinceLastPress > 200) {
if (isValueSelected) {
// Save settings to EEPROM only when transitioning from selected value to menu navigation
saveSettingsToEEPROM();
}
isValueSelected = !isValueSelected;
drawMenu();
timeSinceLastPress = 0; // reset the timer
}
}
void adjustValue(int dir) {
switch(menuSelected) {
case 0:
hp1_lowcut += 10 * dir;
hp1_lowcut = constrain(hp1_lowcut, 20.0f, 210.0f);
hp1.setBiQuadEq(0,'H',hp1_lowcut,1,0); // No gain for high-pass filter
break;
case 1:
eq1_lowf += 10 * dir;
eq1_lowf = constrain(eq1_lowf, 20.0f, 20000.0f);
eq1.setBiQuadEq(0,'P',eq1_lowf,eq1_lowq,eq1_lowgain);
break;
case 2:
eq1_lowq += 0.1 * dir;
eq1_lowq = constrain(eq1_lowq, 0.1f, 10.0f);
eq1.setBiQuadEq(0,'P',eq1_lowf,eq1_lowq,eq1_lowgain);
break;
case 3:
eq1_lowgain += 1 * dir;
eq1_lowgain = constrain(eq1_lowgain, -24.0f, 24.0f);
eq1.setBiQuadEq(0,'P',eq1_lowf,eq1_lowq,eq1_lowgain);
break;
case 4:
eq2_lowf += 10 * dir;
eq2_lowf = constrain(eq2_lowf, 20.0f, 20000.0f);
eq1.setBiQuadEq(1,'P',eq2_lowf,eq2_lowq,eq2_lowgain);
break;
case 5:
eq2_lowq += 0.1 * dir;
eq2_lowq = constrain(eq2_lowq, 0.1f, 10.0f);
eq1.setBiQuadEq(1,'P',eq2_lowf,eq2_lowq,eq2_lowgain);
break;
case 6:
eq2_lowgain += 1 * dir;
eq2_lowgain = constrain(eq2_lowgain, -24.0f, 24.0f);
eq1.setBiQuadEq(1,'P',eq2_lowf,eq2_lowq,eq2_lowgain);
break;
}
drawMenu();
}
void drawMenu() {
// If no previous menu was selected, draw all menu items
if (oldMenuSelected == -1) {
for (int i = 0; i < 7; i++) {
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(10, i * 30 + 10);
tft.println(menuItems[i]);
printMenuItemValue(i);
}
} else {
// Redraw the old menu item in regular style
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(10, oldMenuSelected * 30 + 10);
tft.println(menuItems[oldMenuSelected]);
printMenuItemValue(oldMenuSelected);
}
// Draw the current menu item in selected style
if (isValueSelected) {
tft.setTextColor(ILI9341_RED, ILI9341_RED);
} else {
tft.setTextColor(ILI9341_RED, ILI9341_BLACK);
}
tft.setCursor(10, menuSelected * 30 + 10);
tft.println(menuItems[menuSelected]);
printMenuItemValue(menuSelected);
oldMenuSelected = menuSelected;
}
void printMenuItemValue(int menuItem) {
float value = 0;
switch(menuItem) {
case 0: value = hp1_lowcut; break;
case 1: value = eq1_lowf; break;
case 2: value = eq1_lowq; break;
case 3: value = eq1_lowgain; break;
case 4: value = eq2_lowf; break;
case 5: value = eq2_lowq; break;
case 6: value = eq2_lowgain; break;
}
int x = 200;
int y = menuItem * 30 + 10;
int width = 50;
int height = 28;
// Clear the area
tft.fillRect(x, y, width, height, ILI9341_BLACK);
if (menuItem == menuSelected && isValueSelected) {
tft.setTextColor(ILI9341_RED, ILI9341_BLACK); // Change text color for highlighted value
} else {
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
}
tft.setCursor(x, y);
switch (menuItem) {
case 0:
case 1:
case 3:
tft.print(value, 0);
break;
case 2:
tft.print(value, 1);
break;
case 4:
case 6:
tft.print(value, 0);
break;
case 5:
tft.print(value, 1);
break;
}
// Reset the text color for other uses
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
}
