Call custom function inside void Setup?

I'm writing code to control an audio processor. Actually asked a different question a few weeks ago. Should I add my new questions to the same thread or is it okay to start a new thread for a new question?

The problem I'm having is that I created my own function (void sendVolume near end of the code) to send the volume level over I2C to the audio processor. Called within void loop it works great - when I rotate the encoder it increments or decrements the value and sends over I2C just as desired. :+1:

What doesn't work is sending a default 'safe' volume level of -50dB once at the start of the code to ensure when the system is reset it doesn't blow my ears off. I set the default volume in void setup and then followed with sendVolume(); (see after "// Setup I2C") but it doesn't seem to send the volume to the audio processor.

Tips on why this is not working? Does the wire command need more time to establish or can I not call my own functions inside void setup?

#include <Wire.h> // I2C library.

// 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 mode change variables
int mode = 1;  // Mode variable used to run switch case. 1 = volume, 2 = audio input, 3 = tone control.
int currentInputNumber = 1; // Audio input the system is currently set to.
int newInputNumber = 1; // 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.

// 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; // 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


  volume = -50; // Set default volume level to -50dB.    
  sendVolume(); // Send volume to audio processor.

} 

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.
      }

      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.
      }

      if (buttonPressed == 1) { // If button is pressed.
        mode = mode + 1 ; // Progresses to the next page (case 2).
        updateDisplay();  // Print to serial monitor.
      }

      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.
      }

      if (encCW == 1) { // If the rotary encoder has been rotated clockwise.
        newInputNumber += 1; // Add 1 to the input number. 

        if (newInputNumber >= 6){ // Limit input selection. If input number is more than 5.
          newInputNumber = 1; // Loop back to 1.
        }

        updateDisplay();  // Print to serial monitor.
      }

      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.
        }

        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.
        }

      }

      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.
      }

      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.
      }

        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.
            }

            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.
            }

        }

      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.
    }
}

Probably because of that.

(They're called "functions", not "voids")

modify your function like this

void sendVolume(bool forceSend = false) { // 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 (forceSend || (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.
  }
}

and at the end of the setup() do

  sendVolume(true); // Send volume to audio processor.

or quick hack, just give a value in the past to lastSafeWrite
unsigned long lastSafeWrite = -10;
so that the test will be true the first time you call

this could be made into a 2D Array

and you could initialise everything statically instead of inside the setup()

byte safeDataAddr[][2] = {
  {0x69, 0x00},
  {0x60, 0x02},
  {0x60, 0x02},
  {0x60, 0x03},
  {0x60, 0x04},
};

// 2-byte address to send target address during a safe load.
byte safeTargetAddr[] = {0x60, 0x05};

 // 2-byte address to send number of words to write and trigger the safe load.
byte safeTrigAddr[] = {0x60, 0x06};

// Trigger safe load of 1 data word.
byte safeTrig1[] = {0x00, 0x00, 0x00, 0x01};

// 4-byte address of volume control.
byte volAddress[] = {0x00, 0x00, 0x00, 0x19}; 

I see a lot of 0x60 and successive numbers so wonder if the arrays are really needed

Thanks, that fixed it! Kind of embarrassed I didn't realise it myself.

J-M-L I'll have to read up on 2D arrays, thanks for the tip.