Hello again!
I'm using the Adafruit libraries to display text on a 1.3" OLED. I have a char array called inputName (declared under "Menu Variables") where some of the elements are longer than others.
I have a function that writes to the OLED called "sendDisplay" (bottom of code) and I have tried to implement some code found HERE using display.getTextBounds.
The strange thing is, it only centres correctly if the code is run twice. In Setup if run sendDisplay once it places the text "ANALOG 1" to the right of centre.
display.clearDisplay(); // Clear the display.
sendDisplay(); // Update the display with initial settings.
Full Code:
#include <Wire.h> // I2C library.
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// OLED screen setup.
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C /// Screen I2C address, 7-bit.
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Rotary encoder variables
const byte inputCLK = 3; // Arduino pin the rotary encoder CLK pin is connected to.
const byte inputDT = 2; // Arduino pin the rotary encoder DT pin is connected to.
int currentStateCLK; // Current state of rotary encoder pin CLK. Used to identify rotation.
int previousStateCLK; // Current state of rotary encoder pin CLK. Used to identify rotation.
boolean encCCW; // Rotary encoder flag to denote counter-clockwise rotatation by one pulse.
boolean encCW; // Rotary encoder flag to denote clockwise rotatation by one pulse.
// Push button variables
const byte buttonPin = 4; // Arduino pin the push button is connected to.
byte previousButtonState = HIGH; // Assume switch open because of pull-up resistor.
byte currentButtonState; // Will be populated by reading button pin satae.
const unsigned long debounceTime = 5; // Milliseconds of debounce.
unsigned long buttonPressTime; // Time since button last changed state. Used to differentialte button bounce vs. intentional press.
boolean buttonPressed = 0; // A flag variable to identify button presses. Returned from button debounce code.
// Menu variables
int mode = 1; // Mode variable used to run switch case. 1 = volume, 2 = audio input, 3 = tone control.
int currentInputNumber = 0; // Audio input the system is currently set to.
int newInputNumber = 0; // Audio input user is in the process of choosing by rotary control.
int currentToneValue = 5; // Current tone control setting.
int newToneValue = 5; // Tone control setting the user is in the process of choosing by rotary control.
char inputName[][10] = {"ANALOG 1", "ANALOG 2", "ANALOG 3", "DIGITAL 1", "DIGITAL 2", "DIGITAL 3"};
// Gain variables
float volume; // Volume level in dB.
float linGain; // Volume level as a linear gain value between 0 (no level) and 1 (full level).
uint32_t vol32; // Linear gain as a 32 bit word to populate hex array.
byte volArray[4]; // 4 byte array to hold volume hex values ready to send over I2C to the audio processor.
// Audio processor safeload addresses.
byte safeDataAddr0[2]; // 2-byte address to send first data word during a safe load.
byte safeDataAddr1[2]; // 2-byte address to send second data word during a safe load.
byte safeDataAddr2[2]; // 2-byte address to send third data word during a safe load.
byte safeDataAddr3[2]; // 2-byte address to send fourth data word during a safe load.
byte safeDataAddr4[2]; // 2-byte address to send fith data word during a safe load.
byte safeTargetAddr[2]; // 2-byte address to send target address during a safe load.
byte safeTrigAddr[2]; // 2-byte address to send number of words to write and trigger the safe load.
byte safeTrig1[4]; // Trigger safe load of 1 data word.
int dspAdd = 59; // Address of audio processor.
byte volAddress[4]; // 4-byte address of volume control.
unsigned long lastSafeWrite = -100; // Time at which the last safeload write was completed.
void setup() {
//Below addresses are fixed for safe load operations, do not change.
safeDataAddr0[0] = 0x60;
safeDataAddr0[1] = 0x00;
safeDataAddr1[0] = 0x60;
safeDataAddr1[1] = 0x01;
safeDataAddr2[0] = 0x60;
safeDataAddr2[1] = 0x02;
safeDataAddr3[0] = 0x60;
safeDataAddr3[1] = 0x03;
safeDataAddr4[0] = 0x60;
safeDataAddr4[1] = 0x04;
safeTargetAddr[0] = 0x60;
safeTargetAddr[1] = 0x05;
safeTrigAddr[0] = 0x60;
safeTrigAddr[1] = 0x06;
// Below variables are user configurable.
volAddress[0] = 0x00; // Address of the volume module. Check in Sigma Studio.
volAddress[1] = 0x00;
volAddress[2] = 0x00;
volAddress[3] = 0x19;
safeTrig1[0] = 0x00; // Safeload trigger for 1 data word.
safeTrig1[1] = 0x00;
safeTrig1[2] = 0x00;
safeTrig1[3] = 0x01;
// Set encoder and push button pins as inputs.
pinMode (inputCLK,INPUT);
pinMode (inputDT,INPUT);
pinMode (buttonPin, INPUT_PULLUP);
encCCW = 0; // Initial state for rotation flag not rotated.
encCW = 0; // Initial state for rotation flag not rotated.
previousStateCLK = digitalRead(inputCLK); // Read initial state of rotary encoder CLK pin. Assign to previousStateCLK so we can check for state changes.
// Setup Serial Monitor
Serial.begin (9600);
// Setup I2C.
Wire.begin(); // join i2c bus
Wire.setClock(400000); // Set 400KHz frequency
// Begin display. SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
volume = -50; // Set default volume level to -50dB.
sendVolume(); // Send volume to audio processor.
display.clearDisplay(); // Clear the display.
sendDisplay(); // Update the display with initial settings.
}
void loop() {
// Rotary encoder code.
currentStateCLK = digitalRead(inputCLK); // Read the current state of inputCLK pin.
if (currentStateCLK != previousStateCLK) { // If the previous and the current state of inputCLK are different, then the encoder and been rotated.
if (currentStateCLK != digitalRead(inputDT)) { // If the inputCLK state is different than the inputDT state then the rotation is counterclockwise.
encCCW = 1; // Set rotation flag counter-clockwise.
}
else { // If inputCLK and inputDT are the same then encoder is not rotating CCW.
encCW = 1; // Set rotation flag clockwise.
}
}
// Button press code with debounce.
currentButtonState = digitalRead (buttonPin);
if (currentButtonState != previousButtonState){ // If the button has changed state since the last loop.
if (millis () - buttonPressTime >= debounceTime){ // If the current time minus the time when the switch was pressed (i.e. how long since the switch chagned state) is more than the debounce period. (This statement acts on the leading edge of a switch press, but rejects any state changes that happen within 10ms after that).
buttonPressTime = millis (); // Set switch press time = now.
previousButtonState = currentButtonState; // Save switch state for next loop.
if (currentButtonState == LOW){ // When debounce criteria are met and button is still pressed.
buttonPressed = 1; // Set button flag to 1.
}
else { // When debounce criteria are not met because boutton is not pressed or it is bouncing.
buttonPressed = 0; // Set button flag to 0.
}
}
}
// Limit Mode (menu page) and loop back to main menu.
if (mode >= 4){ // If mode is more than 3.
mode = 1; // Loop back to 1.
}
switch (mode) { // Each menu page is a case operated by the 'mode' variable.
case 1: // Main menu page, volume control.
if (encCCW == 1) { // If the rotary encoder has been rotated counter-clockwise.
volume = volume - 2; // Decrement volume level by one.
if (volume <= -60){ // Limit volume level. If volume is less than -60dB.
volume = -60; // Make it -60dB.
}
sendVolume(); // Send volume over i2C.
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (encCW == 1) { // If the rotary encoder has been rotated clockwise.
volume = volume + 2; // Increment volume level by one.
if (volume >= 0){ // Limit volume level. If volume is greater than 0dB.
volume = 0; // Make volume 0dB.
}
sendVolume(); // Send volume over i2C.
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (buttonPressed == 1) { // If button is pressed.
mode = mode + 1 ; // Progresses to the next page (case 2).
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
buttonPressed = 0; // Reset button press for next loop.
break; // End of case 1.
case 2:
if (encCCW == 1) { // If the rotary encoder has been rotated counter-clockwise.
newInputNumber -= 1; // subtract 1 from input number.
if (newInputNumber < 0){ // Limit input selection. If input number is less than 1.
newInputNumber = 5; // Loop back to 5.
}
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (encCW == 1) { // If the rotary encoder has been rotated clockwise.
newInputNumber += 1; // Add 1 to the input number.
if (newInputNumber > 5){ // Limit input selection. If input number is more than 5.
newInputNumber = 0; // Loop back to 1.
}
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (buttonPressed == 1) { // If button is pressed.
if (newInputNumber == currentInputNumber){ // Without changing input selection.
mode = mode + 1 ; // Increment mode by 1 to progress to the next menu page.
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
else { // A new audio input is being selected.
currentInputNumber = newInputNumber; // Set the current audio input to the new selection.
mode = 1; // Go back to the main menu page (case 1)
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
}
buttonPressed = 0; // Reset button press for next loop.
break; // End of case 2.
case 3:
if (encCCW == 1) { // If the rotary encoder has been rotated counter-clockwise.
newToneValue -= 1; // Subtract 1 from tone value.
if (newToneValue <= 0){ // Limit tone value. If tone value is less than 1.
newToneValue = 1; // Make it 1.
}
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (encCW == 1) { // If the rotary encoder has been rotated clockwise.
newToneValue += 1; // Add 1 to the tone value.
if (newToneValue >= 10){ // Limit tone value. If tone value is more than 9.
newToneValue = 9; // Make it 9.
}
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (buttonPressed == 1) { // If button is pressed.
if (newToneValue == currentToneValue){ // Without changing tone control value.
mode = 1 ; // Go back to the main menu page, because this is the last menu.
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
else{ // A new tone value has been selected.
currentToneValue = newToneValue; // Set the current tone value to the new tone value.
mode = 1; // Go back to the main menu page (case 1).
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
}
buttonPressed = 0; // Reset button press for next loop.
break; // End of case 3.
} // End of switch.
// Rotary encoder reset values for next loop.
previousStateCLK = currentStateCLK; // Update previousStateCLK with the current state of inputCLK.
encCCW = 0; // Reset encoder counter-clockwise flag.
encCW = 0; // Reset encoder clockwise flag.
} // end of loop.
void updateDisplay(){ // Display the menu mode, volume level and current audio input.
Serial.print("Mode:");
Serial.println(mode);
Serial.print("Volume: ");
Serial.print(volume);
Serial.println("dB");
Serial.print("Current Input Number:");
Serial.println(currentInputNumber);
Serial.print("New Input Number:");
Serial.println(newInputNumber);
Serial.print("Current Tone Control Value:");
Serial.println(currentToneValue);
Serial.print("New Tone Control Value:");
Serial.println(newToneValue);
Serial.println();
}
void sendVolume(){ // Convert dB volume level to hex array and send by I2C to the audio processor.
// Convery dB level to hex array.
linGain = pow(10, volume/20); // Convert dB level to linear gain value.
vol32 = linGain * 16777216; // Convert linear gain value stored as a float variable to a 32 bit integer.
volArray[0] = (vol32 >> 24) & 0xFF; // Populate first byte of array with most significant 8 bits of the 32 bit volume word, by shifting word right by 24 bits. Adding 0xFF denotes it as a hex value.
volArray[1] = (vol32 >> 16) & 0xFF; // Populate second byte of array with next most significant 8 bits of the 32 bit volume word, by shifting word right by 16 bits. Adding 0xFF denotes it as a hex value.
volArray[2] = (vol32 >> 8) & 0xFF; // Populate third byte of array with next most significant 8 bits of the 32 bit volume word, by shifting word right by 8 bits. Adding 0xFF denotes it as a hex value.
volArray[3] = vol32 & 0xFF; // Populate fourth byte of array with least significant 8 bits of the 32 bit volume word, naturally the first 8 bits of the word. Adding 0xFF denotes it as a hex value.
// Send hex array by I2C safeload proceedure to audio processor.
if (millis () - lastSafeWrite >= 1){ // Safeload should only occur once per audio frame (1ms is pleanty).
Serial.println("Sending Volume Now");
Wire.beginTransmission(dspAdd); // Begin I2C transmission to 7-bit address of audio processor.
Wire.write(safeDataAddr0, 2); // Prepare to write safe load data bank 1.
Wire.write(volArray, 4); // Write hex array holding linear volume level.
Wire.endTransmission(); // Send data queue and end transmission with stop bit.
Wire.beginTransmission(dspAdd); // Begin I2C transmission to 7-bit address (0x3B) adds R/W bit automatically.
Wire.write(safeTargetAddr, 2); // Prepare to write target address.
Wire.write(volAddress, 4); // Write aadress of volume control module.
Wire.endTransmission(); // Send data queue and end transmission with stop bit.
Wire.beginTransmission(dspAdd); // Begin I2C transmission to 7-bit address (0x3B) adds R/W bit automatically.
Wire.write(safeTrigAddr, 2); // Prepare to writing number of data banks used (1-5) and trigger safe load.
Wire.write(safeTrig1, 4); // Trigger safe load writing 1 data bank.
Wire.endTransmission(); // Send data queue and end transmission with stop bit.
lastSafeWrite = millis (); // Store time when the latest write occured.
}
}
void sendDisplay(){
int16_t x1, y1;
uint16_t width, height;
display.getTextBounds(inputName[currentInputNumber], 0, 0, &x1, &y1, &width, &height);
if (mode == 1) {
display.clearDisplay(); // Clear the display.
display.setTextSize(1); // Draw 1X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(2, 0); // Position.
display.print(F("CLIP ")); // Text.
display.print(F("DRC ")); // Text.
display.println(F("AMP 34c")); // Text.
display.print(F("---------------------")); // Text.
display.setTextSize(4); // Draw 4X-scale text
display.setCursor(5, 17); // Position.
display.print(volume, 0); // Text.
display.print(F("dB"));
display.setTextSize(2); // Draw 4X-scale text
display.setCursor((SCREEN_WIDTH - width) / 2, 50);
display.print(inputName[newInputNumber]); // Text.
display.display(); // Send to display.
}
if (mode == 2) {
display.clearDisplay(); // Clear the display.
display.setTextSize(1); // Draw 1X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(2, 0); // Position.
display.print(F("CLIP ")); // Text.
display.print(F("DRC ")); // Text.
display.println(F("AMP 34c")); // Text.
display.print(F("---------------------")); // Text.
display.setTextSize(2); // Draw 3X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 15); // Position.
display.println(F("Input")); // Text.
display.setTextSize(2); // Draw 4X-scale text
display.setCursor(0, 30); // Position.
display.print(inputName[newInputNumber]); // Text.
display.display(); // Send to display.
}
if (mode == 3) {
display.clearDisplay(); // Clear the display.
display.setTextSize(3); // Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(28, 5); // Position.
display.println(F("Tone")); // Text.
display.setTextSize(3); // Draw 4X-scale text
display.setCursor(60, 30); // Position.
display.print(newToneValue); // Text.
display.display(); // Send to display.
}
}
Yet if I simply run the function twice in a row, it works wonderfully and places the text in horizontal centre!
display.clearDisplay(); // Clear the display.
sendDisplay(); // Update the display with initial settings.
sendDisplay(); // Update the display with initial settings.
Full Code:
#include <Wire.h> // I2C library.
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// OLED screen setup.
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C /// Screen I2C address, 7-bit.
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Rotary encoder variables
const byte inputCLK = 3; // Arduino pin the rotary encoder CLK pin is connected to.
const byte inputDT = 2; // Arduino pin the rotary encoder DT pin is connected to.
int currentStateCLK; // Current state of rotary encoder pin CLK. Used to identify rotation.
int previousStateCLK; // Current state of rotary encoder pin CLK. Used to identify rotation.
boolean encCCW; // Rotary encoder flag to denote counter-clockwise rotatation by one pulse.
boolean encCW; // Rotary encoder flag to denote clockwise rotatation by one pulse.
// Push button variables
const byte buttonPin = 4; // Arduino pin the push button is connected to.
byte previousButtonState = HIGH; // Assume switch open because of pull-up resistor.
byte currentButtonState; // Will be populated by reading button pin satae.
const unsigned long debounceTime = 5; // Milliseconds of debounce.
unsigned long buttonPressTime; // Time since button last changed state. Used to differentialte button bounce vs. intentional press.
boolean buttonPressed = 0; // A flag variable to identify button presses. Returned from button debounce code.
// Menu variables
int mode = 1; // Mode variable used to run switch case. 1 = volume, 2 = audio input, 3 = tone control.
int currentInputNumber = 0; // Audio input the system is currently set to.
int newInputNumber = 0; // Audio input user is in the process of choosing by rotary control.
int currentToneValue = 5; // Current tone control setting.
int newToneValue = 5; // Tone control setting the user is in the process of choosing by rotary control.
char inputName[][10] = {"ANALOG 1", "ANALOG 2", "ANALOG 3", "DIGITAL 1", "DIGITAL 2", "DIGITAL 3"};
// Gain variables
float volume; // Volume level in dB.
float linGain; // Volume level as a linear gain value between 0 (no level) and 1 (full level).
uint32_t vol32; // Linear gain as a 32 bit word to populate hex array.
byte volArray[4]; // 4 byte array to hold volume hex values ready to send over I2C to the audio processor.
// Audio processor safeload addresses.
byte safeDataAddr0[2]; // 2-byte address to send first data word during a safe load.
byte safeDataAddr1[2]; // 2-byte address to send second data word during a safe load.
byte safeDataAddr2[2]; // 2-byte address to send third data word during a safe load.
byte safeDataAddr3[2]; // 2-byte address to send fourth data word during a safe load.
byte safeDataAddr4[2]; // 2-byte address to send fith data word during a safe load.
byte safeTargetAddr[2]; // 2-byte address to send target address during a safe load.
byte safeTrigAddr[2]; // 2-byte address to send number of words to write and trigger the safe load.
byte safeTrig1[4]; // Trigger safe load of 1 data word.
int dspAdd = 59; // Address of audio processor.
byte volAddress[4]; // 4-byte address of volume control.
unsigned long lastSafeWrite = -100; // Time at which the last safeload write was completed.
void setup() {
//Below addresses are fixed for safe load operations, do not change.
safeDataAddr0[0] = 0x60;
safeDataAddr0[1] = 0x00;
safeDataAddr1[0] = 0x60;
safeDataAddr1[1] = 0x01;
safeDataAddr2[0] = 0x60;
safeDataAddr2[1] = 0x02;
safeDataAddr3[0] = 0x60;
safeDataAddr3[1] = 0x03;
safeDataAddr4[0] = 0x60;
safeDataAddr4[1] = 0x04;
safeTargetAddr[0] = 0x60;
safeTargetAddr[1] = 0x05;
safeTrigAddr[0] = 0x60;
safeTrigAddr[1] = 0x06;
// Below variables are user configurable.
volAddress[0] = 0x00; // Address of the volume module. Check in Sigma Studio.
volAddress[1] = 0x00;
volAddress[2] = 0x00;
volAddress[3] = 0x19;
safeTrig1[0] = 0x00; // Safeload trigger for 1 data word.
safeTrig1[1] = 0x00;
safeTrig1[2] = 0x00;
safeTrig1[3] = 0x01;
// Set encoder and push button pins as inputs.
pinMode (inputCLK,INPUT);
pinMode (inputDT,INPUT);
pinMode (buttonPin, INPUT_PULLUP);
encCCW = 0; // Initial state for rotation flag not rotated.
encCW = 0; // Initial state for rotation flag not rotated.
previousStateCLK = digitalRead(inputCLK); // Read initial state of rotary encoder CLK pin. Assign to previousStateCLK so we can check for state changes.
// Setup Serial Monitor
Serial.begin (9600);
// Setup I2C.
Wire.begin(); // join i2c bus
Wire.setClock(400000); // Set 400KHz frequency
// Begin display. SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
volume = -50; // Set default volume level to -50dB.
sendVolume(); // Send volume to audio processor.
display.clearDisplay(); // Clear the display.
sendDisplay(); // Update the display with initial settings.
sendDisplay(); // Update the display with initial settings.
}
void loop() {
// Rotary encoder code.
currentStateCLK = digitalRead(inputCLK); // Read the current state of inputCLK pin.
if (currentStateCLK != previousStateCLK) { // If the previous and the current state of inputCLK are different, then the encoder and been rotated.
if (currentStateCLK != digitalRead(inputDT)) { // If the inputCLK state is different than the inputDT state then the rotation is counterclockwise.
encCCW = 1; // Set rotation flag counter-clockwise.
}
else { // If inputCLK and inputDT are the same then encoder is not rotating CCW.
encCW = 1; // Set rotation flag clockwise.
}
}
// Button press code with debounce.
currentButtonState = digitalRead (buttonPin);
if (currentButtonState != previousButtonState){ // If the button has changed state since the last loop.
if (millis () - buttonPressTime >= debounceTime){ // If the current time minus the time when the switch was pressed (i.e. how long since the switch chagned state) is more than the debounce period. (This statement acts on the leading edge of a switch press, but rejects any state changes that happen within 10ms after that).
buttonPressTime = millis (); // Set switch press time = now.
previousButtonState = currentButtonState; // Save switch state for next loop.
if (currentButtonState == LOW){ // When debounce criteria are met and button is still pressed.
buttonPressed = 1; // Set button flag to 1.
}
else { // When debounce criteria are not met because boutton is not pressed or it is bouncing.
buttonPressed = 0; // Set button flag to 0.
}
}
}
// Limit Mode (menu page) and loop back to main menu.
if (mode >= 4){ // If mode is more than 3.
mode = 1; // Loop back to 1.
}
switch (mode) { // Each menu page is a case operated by the 'mode' variable.
case 1: // Main menu page, volume control.
if (encCCW == 1) { // If the rotary encoder has been rotated counter-clockwise.
volume = volume - 2; // Decrement volume level by one.
if (volume <= -60){ // Limit volume level. If volume is less than -60dB.
volume = -60; // Make it -60dB.
}
sendVolume(); // Send volume over i2C.
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (encCW == 1) { // If the rotary encoder has been rotated clockwise.
volume = volume + 2; // Increment volume level by one.
if (volume >= 0){ // Limit volume level. If volume is greater than 0dB.
volume = 0; // Make volume 0dB.
}
sendVolume(); // Send volume over i2C.
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (buttonPressed == 1) { // If button is pressed.
mode = mode + 1 ; // Progresses to the next page (case 2).
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
buttonPressed = 0; // Reset button press for next loop.
break; // End of case 1.
case 2:
if (encCCW == 1) { // If the rotary encoder has been rotated counter-clockwise.
newInputNumber -= 1; // subtract 1 from input number.
if (newInputNumber < 0){ // Limit input selection. If input number is less than 1.
newInputNumber = 5; // Loop back to 5.
}
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (encCW == 1) { // If the rotary encoder has been rotated clockwise.
newInputNumber += 1; // Add 1 to the input number.
if (newInputNumber > 5){ // Limit input selection. If input number is more than 5.
newInputNumber = 0; // Loop back to 1.
}
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (buttonPressed == 1) { // If button is pressed.
if (newInputNumber == currentInputNumber){ // Without changing input selection.
mode = mode + 1 ; // Increment mode by 1 to progress to the next menu page.
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
else { // A new audio input is being selected.
currentInputNumber = newInputNumber; // Set the current audio input to the new selection.
mode = 1; // Go back to the main menu page (case 1)
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
}
buttonPressed = 0; // Reset button press for next loop.
break; // End of case 2.
case 3:
if (encCCW == 1) { // If the rotary encoder has been rotated counter-clockwise.
newToneValue -= 1; // Subtract 1 from tone value.
if (newToneValue <= 0){ // Limit tone value. If tone value is less than 1.
newToneValue = 1; // Make it 1.
}
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (encCW == 1) { // If the rotary encoder has been rotated clockwise.
newToneValue += 1; // Add 1 to the tone value.
if (newToneValue >= 10){ // Limit tone value. If tone value is more than 9.
newToneValue = 9; // Make it 9.
}
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
if (buttonPressed == 1) { // If button is pressed.
if (newToneValue == currentToneValue){ // Without changing tone control value.
mode = 1 ; // Go back to the main menu page, because this is the last menu.
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
else{ // A new tone value has been selected.
currentToneValue = newToneValue; // Set the current tone value to the new tone value.
mode = 1; // Go back to the main menu page (case 1).
updateDisplay(); // Print to serial monitor.
sendDisplay();
}
}
buttonPressed = 0; // Reset button press for next loop.
break; // End of case 3.
} // End of switch.
// Rotary encoder reset values for next loop.
previousStateCLK = currentStateCLK; // Update previousStateCLK with the current state of inputCLK.
encCCW = 0; // Reset encoder counter-clockwise flag.
encCW = 0; // Reset encoder clockwise flag.
} // end of loop.
void updateDisplay(){ // Display the menu mode, volume level and current audio input.
Serial.print("Mode:");
Serial.println(mode);
Serial.print("Volume: ");
Serial.print(volume);
Serial.println("dB");
Serial.print("Current Input Number:");
Serial.println(currentInputNumber);
Serial.print("New Input Number:");
Serial.println(newInputNumber);
Serial.print("Current Tone Control Value:");
Serial.println(currentToneValue);
Serial.print("New Tone Control Value:");
Serial.println(newToneValue);
Serial.println();
}
void sendVolume(){ // Convert dB volume level to hex array and send by I2C to the audio processor.
// Convery dB level to hex array.
linGain = pow(10, volume/20); // Convert dB level to linear gain value.
vol32 = linGain * 16777216; // Convert linear gain value stored as a float variable to a 32 bit integer.
volArray[0] = (vol32 >> 24) & 0xFF; // Populate first byte of array with most significant 8 bits of the 32 bit volume word, by shifting word right by 24 bits. Adding 0xFF denotes it as a hex value.
volArray[1] = (vol32 >> 16) & 0xFF; // Populate second byte of array with next most significant 8 bits of the 32 bit volume word, by shifting word right by 16 bits. Adding 0xFF denotes it as a hex value.
volArray[2] = (vol32 >> 8) & 0xFF; // Populate third byte of array with next most significant 8 bits of the 32 bit volume word, by shifting word right by 8 bits. Adding 0xFF denotes it as a hex value.
volArray[3] = vol32 & 0xFF; // Populate fourth byte of array with least significant 8 bits of the 32 bit volume word, naturally the first 8 bits of the word. Adding 0xFF denotes it as a hex value.
// Send hex array by I2C safeload proceedure to audio processor.
if (millis () - lastSafeWrite >= 1){ // Safeload should only occur once per audio frame (1ms is pleanty).
Serial.println("Sending Volume Now");
Wire.beginTransmission(dspAdd); // Begin I2C transmission to 7-bit address of audio processor.
Wire.write(safeDataAddr0, 2); // Prepare to write safe load data bank 1.
Wire.write(volArray, 4); // Write hex array holding linear volume level.
Wire.endTransmission(); // Send data queue and end transmission with stop bit.
Wire.beginTransmission(dspAdd); // Begin I2C transmission to 7-bit address (0x3B) adds R/W bit automatically.
Wire.write(safeTargetAddr, 2); // Prepare to write target address.
Wire.write(volAddress, 4); // Write aadress of volume control module.
Wire.endTransmission(); // Send data queue and end transmission with stop bit.
Wire.beginTransmission(dspAdd); // Begin I2C transmission to 7-bit address (0x3B) adds R/W bit automatically.
Wire.write(safeTrigAddr, 2); // Prepare to writing number of data banks used (1-5) and trigger safe load.
Wire.write(safeTrig1, 4); // Trigger safe load writing 1 data bank.
Wire.endTransmission(); // Send data queue and end transmission with stop bit.
lastSafeWrite = millis (); // Store time when the latest write occured.
}
}
void sendDisplay(){
int16_t x1, y1;
uint16_t width, height;
display.getTextBounds(inputName[currentInputNumber], 0, 0, &x1, &y1, &width, &height);
if (mode == 1) {
display.clearDisplay(); // Clear the display.
display.setTextSize(1); // Draw 1X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(2, 0); // Position.
display.print(F("CLIP ")); // Text.
display.print(F("DRC ")); // Text.
display.println(F("AMP 34c")); // Text.
display.print(F("---------------------")); // Text.
display.setTextSize(4); // Draw 4X-scale text
display.setCursor(5, 17); // Position.
display.print(volume, 0); // Text.
display.print(F("dB"));
display.setTextSize(2); // Draw 4X-scale text
display.setCursor((SCREEN_WIDTH - width) / 2, 50);
display.print(inputName[newInputNumber]); // Text.
display.display(); // Send to display.
}
if (mode == 2) {
display.clearDisplay(); // Clear the display.
display.setTextSize(1); // Draw 1X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(2, 0); // Position.
display.print(F("CLIP ")); // Text.
display.print(F("DRC ")); // Text.
display.println(F("AMP 34c")); // Text.
display.print(F("---------------------")); // Text.
display.setTextSize(2); // Draw 3X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 15); // Position.
display.println(F("Input")); // Text.
display.setTextSize(2); // Draw 4X-scale text
display.setCursor(0, 30); // Position.
display.print(inputName[newInputNumber]); // Text.
display.display(); // Send to display.
}
if (mode == 3) {
display.clearDisplay(); // Clear the display.
display.setTextSize(3); // Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(28, 5); // Position.
display.println(F("Tone")); // Text.
display.setTextSize(3); // Draw 4X-scale text
display.setCursor(60, 30); // Position.
display.print(newToneValue); // Text.
display.display(); // Send to display.
}
}