Teensy 3.2 + Audio board save settings / adjustments in EEPROM

Helmut

Member
I want to store all settings (volume, mix input signals and frequency) in EEPROM so that all settings are saved in case of power failure and Teensy + Audio board will boot with the saved settings.
Volume = OKAY
Mix input signals = OKAY
Frequency = unfortunately no :confused: -> If I adjust the frequency with a filter setting (low pass, band pass or high pass) and reboot the Teensy, then some frequency comes but not which I have set.

Where do I have the error?

my sketch:

Code:
// 
#include <EEPROM.h>
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>


// save setings to EEPROM
#define SETTING_VER       "Helmut.V1.0"  // Settings Version

typedef struct {                              // Setting Structure
  int knobF;                                  // int Frequence
  int knobM;                                  // int Mix White and Pink Noise
  int knobV;                                  // int Volume
  char  ver[sizeof(SETTING_VER)];
}
SettingType;

SettingType settings = {           // Default settings
  50,
  125,
  80,
  SETTING_VER
};

bool loadConfiguration() {
  char savedVersion[sizeof(SETTING_VER)];
  int eepromVersionOffset = sizeof(settings) - sizeof(SETTING_VER);  // Get the offset to the saved version
  EEPROM.get(eepromVersionOffset,savedVersion);                      // Get the saved version, if it exist


if (strcmp(savedVersion,SETTING_VER) == 0) {                         // If version in EEPROM is correct, overwrite the default config with eeprom version

   EEPROM.get(0, settings);                                          // Overwrite the structure with EEPROM date
   }
}
 bool saveConfiguration() {

  EEPROM.put(0,settings); 
 }
// end of save to EEPROM



// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=224,278
AudioMixer4              mixer1;         //xy=477,287
AudioFilterStateVariable filter1;        //xy=719,289
AudioMixer4              mixer2;         //xy=905,292
AudioOutputI2S           i2s2;           //xy=1139,295
AudioConnection          patchCord1(i2s1, 0, mixer1, 0);
AudioConnection          patchCord2(i2s1, 1, mixer1, 1);
AudioConnection          patchCord3(mixer1, 0, filter1, 0);
AudioConnection          patchCord4(filter1, 0, mixer2, 0);
AudioConnection          patchCord5(filter1, 1, mixer2, 1);
AudioConnection          patchCord6(filter1, 2, mixer2, 2);
AudioConnection          patchCord7(mixer2, 0, i2s2, 0);
AudioConnection          patchCord8(mixer2, 0, i2s2, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=721,385
// GUItool: end automatically generated code



// Bounce objects to read pushbuttons 
Bounce button0 = Bounce(2, 15);
Bounce button1 = Bounce(3, 15);  // 15 ms debounce time
Bounce button2 = Bounce(4, 15);

void setup() {

  loadConfiguration();
     
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  Serial.begin(9600);
  AudioMemory(30);
  sgtl5000_1.enable();
}

void loop() {

    
  // Update all the button objects
     button0.update();
     button1.update();
     button2.update();
  
  if (button0.fallingEdge()) {
    Serial.println("Button (pin 2) Press");
    Serial.println("Low Pass Signal");
    mixer2.gain(0, 1.0);  // hear low-pass signal
    mixer2.gain(1, 0.0);
    mixer2.gain(2, 0.0);
       
  }
  if (button1.fallingEdge()) {
    Serial.println("Button (pin 3) Press");
    Serial.println("Band Pass Signal");
    mixer2.gain(0, 0.0);
    mixer2.gain(1, 1.0);  // hear band-pass signal
    mixer2.gain(2, 0.0);
    
  }
  if (button2.fallingEdge()) {
    Serial.println("Button (pin 4) Press");
    Serial.println("High Pass Signal");
    mixer2.gain(0, 0.0);
    mixer2.gain(1, 0.0);
    mixer2.gain(2, 1.0);  // hear high-pass signal
    
  }
  
  // read the knobF and adjust the filter frequency
  int knobF = analogRead(A3);
  // quick and dirty equation for exp scale frequency adjust
  float freq =  expf((float)knobF / 150.0) * 10.0 + 80.0;
  filter1.frequency(freq);
  Serial.print("frequency = ");
  Serial.println(freq);
  delay(200);
  settings.knobF = analogRead(A3);

  // read the knobM postion for mixing input1 and input2
  int knobM = analogRead(A6);  // knob = 0 to 1023
  float gain1 = (float)knobM / 1023.0;
  float gain2 = 1.0 - gain1;
  mixer1.gain(0, gain1);
  mixer1.gain(1, gain2);
  Serial.print("P_W-mix = ");
  Serial.println(knobM);
  settings.knobM = analogRead(A6);

 // read the knobV position for setting volume (analog input A2)
  int knobV = analogRead(A2);
  float vol = (float)knobV / 1280.0;
  sgtl5000_1.volume(vol);
  Serial.print("volume = ");
  Serial.println(vol);
  settings.knobV = analogRead(A2);
  
  saveConfiguration();
}
 
First you use real pots, so no real need to save anything.
Also it would be hard mixing settings taken from the pots unless you have motor controlled pots that can be set at startup.
And if you for example wanted to save settings, then you need two modes
1. Where you only use the fixed stored value
2. Where you use the pots to set the value
While in this mode you can have button to store the current
Setting to be recalled, to mode 1 at any time

If you want more freedom and be able to modify the loaded settings then you need to use rotary encoders
as they don't have any fixed value.
 
I see what you mean....here is my sketch using bluetooth with slider and button.

As I have mention before:
Save volume setting in EEPROM and get the right value after reboot -> OKAY -> last setting of volume

Save the settings for the frequency (int adj = EEPROM.read(1);) I also get the correct value, but the frequency which I get is a completely different one than before reboot.

How can I fix this to get the same frequency after a reboot?


Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>
#include <EEPROM.h>
#define HWSERIAL Serial1 // Teensy 3.2 RX1 PIN0 / TX1 PIN1 / Teensy 4.0 RX1 PIN0 / TX1 PIN1


//AudioSynthNoisePink      pink1;              //xy=201,264
AudioInputI2S            i2s1;                 //xy=359,315
AudioFilterStateVariable filter1;              //xy=446,176
AudioFilterStateVariable filter2;              //xy=446,310
AudioMixer4              mixer1;               // mixers to combine wav file and audio shield inputs
AudioMixer4              mixer2;

// Use one of these 3 output types: Digital I2S, Digital S/PDIF, or Analog DAC
AudioOutputI2S           audioOutput;
//AudioOutputSPDIF       audioOutput;
//AudioOutputAnalog      audioOutput;

// wire up the interfaces between audio components with patch cords
// mixer inputs
// AudioConnection         patchCord1(pink1, 0, filter2, 0);   
AudioConnection          patchCord1(i2s1, 0, filter1, 0);          // left  channels into mixer 1
AudioConnection          patchCord3(filter1, 0, mixer1, 0);
AudioConnection          patchCord4(filter1, 1, mixer1, 1);
AudioConnection          patchCord5(filter1, 2, mixer1, 2);

// AudioConnection          patchCord2(pink1, 0, filter2, 0);          // right channels into mixer 2
AudioConnection          patchCord2(i2s1, 1, filter2, 0);
AudioConnection          patchCord6(filter2, 0, mixer2, 0);
AudioConnection          patchCord7(filter2, 1, mixer2, 1);
AudioConnection          patchCord8(filter2, 2, mixer2, 2);

// mixer outputs
AudioConnection          patchCord9(mixer1, 0, audioOutput, 0);
AudioConnection          patchCord10(mixer2, 0, audioOutput, 1);

// object to allow control of the SGTL5000 audio shield settings
AudioControlSGTL5000  sgtl5000_1;


// Variables used for incoming data 
const byte maxDataLength = 20;
char receivedChars[21] ;
boolean newData = false;

const byte SWITCH_PIN[] = {2,3,4}; // 2 = bass, 3 = band, 4 = high


void setup() {
         for (byte pin = 0; pin < 3; pin++) 
         {
            // Set the SWITCH pins for output and make them LOW
         pinMode(SWITCH_PIN[pin], INPUT);  
         digitalWrite(SWITCH_PIN[pin],LOW);
         }
                
        Serial.begin(115200);
              
     
    //  open software serial connection to the Bluetooth module.
        HWSERIAL.begin(115200); 
        Serial.println("HWSERIAL HM-10 started at 115200"); 

        newData = false;

   

  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(30);

    // comment these out if not using the audio adaptor board.
  Serial.print("init audio shield...");
  sgtl5000_1.enable();
  // audioShield.inputSelect(inputChSelect);  // select mic or line-in for audio shield input source
  Serial.println("done.");

  
        Serial.print("EEPROM-V-Wert = ");
        Serial.println(EEPROM.read(0)); 
        
  
  int vol = EEPROM.read(0);
  sgtl5000_1.volume((float)vol/ 255);


        Serial.print("EEPROM-F-Wert = ");
        Serial.println(EEPROM.read(1)); 

  int adj = EEPROM.read(1);
              
        // quick and dirty equation for exp scale frequency adjust
        float freq =  expf((float)adj / 36.0) * 10.0 + 80.0;  // original 150.0) * 10.0 + 80.0; 
        filter1.frequency(freq);
        filter2.frequency(freq);

  /*
  mixer1.gain(0, 0.0);  
  mixer1.gain(1, 1.0);  // default to hearing band-pass signal
  mixer1.gain(2, 0.0);
  mixer1.gain(3, 0.0);
  
  mixer2.gain(0, 0.0);
  mixer2.gain(1, 1.0);
  mixer2.gain(2, 0.0);
  mixer2.gain(3, 0.0);
 
  delay(1000);
*/
}


void loop()  
{
   
    recvWithStartEndMarkers();                // check to see if we have received any new commands
    if (newData)  {   processCommand();  }    // if we have a new command do something about it
}
 


/*
****************************************
* Function processCommand
* parses data commands contained in receivedChars[]
* receivedChars[] has not been checked for errors
* 
* passed:
*  
* global: 
*       receivedChars[]
*       newData
*
* Returns:
*          
* Sets:
*       receivedChars[]
*       newData
*/
void processCommand(){

    Serial.print("receivedChars = ");   Serial.println(receivedChars);
     
    if (receivedChars[0] == 'S')      // do we have a SWITCH command?
    {
        // we know the SWITCH command has a fixed length "S00"
        // and the value at pos 1 is the SWITCH and the value at pos 2 is 0 or 1 (on/off). 
        // 0 and 1 is the same as LOW and HIGH so we can use 0/1 instead of LOW/HIGH
        
        byte SWITCHnum = receivedChars[1] - 48;          // convert ascii to value by subtracting 48
        boolean SWITCHstatus = receivedChars[2] - 48;
        
        Serial.print(SWITCHnum);
        Serial.println(SWITCHstatus);
        
        
       if  (SWITCHnum == 0 && SWITCHstatus == 1) {
       digitalWrite(SWITCH_PIN[0],HIGH);
       Serial.println("Low Pass Signal");
       mixer1.gain(0, 1.0);  // hear low-pass signal
       mixer1.gain(1, 0.0);
       mixer1.gain(2, 0.0);
       mixer2.gain(0, 1.0);
       mixer2.gain(1, 0.0);
       mixer2.gain(2, 0.0);
      
  }
      if  (SWITCHnum == 1 && SWITCHstatus == 1) {
      digitalWrite(SWITCH_PIN[1],HIGH);
      Serial.println("Band Pass Signal");
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 1.0);  // hear band-pass signal
      mixer1.gain(2, 0.0);
      mixer2.gain(0, 0.0);
      mixer2.gain(1, 1.0);
      mixer2.gain(2, 0.0);
     
  }
     if  (SWITCHnum == 2 && SWITCHstatus == 1) {
     digitalWrite(SWITCH_PIN[2],HIGH);
     Serial.println("High Pass Signal");
     mixer1.gain(0, 0.0);
     mixer1.gain(1, 0.0);
     mixer1.gain(2, 1.0);  // hear high-pass signal
     mixer2.gain(0, 0.0);
     mixer2.gain(1, 0.0);
     mixer2.gain(2, 1.0);
          
   }

     if  (SWITCHnum == 0 && SWITCHstatus == 0) {
     digitalWrite(SWITCH_PIN[1],LOW);
     Serial.println("Low Pass Signal");
     
   }

     if  (SWITCHnum == 1 && SWITCHstatus == 0) {
     digitalWrite(SWITCH_PIN[0],LOW);
     Serial.println("Band Pass Signal");
   }
     
     if  (SWITCHnum == 2 && SWITCHstatus == 0) {
     digitalWrite(SWITCH_PIN[2],LOW);
     Serial.println("High Pass Signal");
     
   }
 }

    if (receivedChars[0] == 'V')      // do we have a slider command?
    {
         
        int hundreds = (receivedChars[1]-48) * 100;
        int tens = (receivedChars[2]-48) * 10;
        int units = receivedChars[3]-48;
        int valueV = hundreds + tens + units;
        EEPROM.write(0, valueV);    // Write state to EEPROM
        delay(100); 
                
        Serial.print("Value Slider V = ");
        Serial.println(valueV);
        Serial.print("EEPROM-Wert = ");
        Serial.println(EEPROM.read(0)); 

        int vol = EEPROM.read(0); 
        Serial.print("vol = ");
        Serial.println(vol); 
        
        sgtl5000_1.volume((float)vol/ 255);
        mixer1.gain(0, (float)vol / 255);   // software mixer input channel volume
        mixer1.gain(1, (float)vol / 255);
        mixer2.gain(0, (float)vol / 255);
        mixer2.gain(1, (float)vol / 255);
           
    }  


       if (receivedChars[0] == 'F')      // do we have a slider command?
       {
                 
        // we know the slider command has a fixed length "F255"
        // and the value at pos 1,2 and 3 is the value. 
        // We need to convert the value from 3 ascii characters to a single value
        
   
        int hundreds = (receivedChars[1]-48) * 100;
        int tens = (receivedChars[2]-48) * 10;
        int units = receivedChars[3]-48;
        int valueF = hundreds + tens + units;
        EEPROM.write(1, valueF);
        delay(100);
        
         // read volume control SLIDER_V and set audio shield volume if required
        Serial.print("Value Slider F = ");
        Serial.println(valueF);
        Serial.print("EEPROM-Wert = ");
        Serial.println(EEPROM.read(1)); 

        int adj = EEPROM.read(1);
        Serial.print("adj = ");
        Serial.println(adj);
          
        // quick and dirty equation for exp scale frequency adjust
        float freq =  expf((float)adj / 36.0) * 10.0 + 80.0;  // original 150.0) * 10.0 + 80.0; 
        filter1.frequency(freq);
        filter2.frequency(freq);
        Serial.print("frequency = ");
        Serial.println(freq);
        delay(200);
        
  }
        
 
    receivedChars[0] = '\0';
    newData = false;
}



// function recvWithStartEndMarkers by Robin2 of the Arduino forums
// See  http://forum.arduino.cc/index.php?topic=288234.0
/*
****************************************
* Function recvWithStartEndMarkers
* reads serial data and returns the content between a start marker and an end marker.
* 
* passed:
*  
* global: 
*       receivedChars[]
*       newData
*
* Returns:
*          
* Sets:
*       newData
*       receivedChars
*
*/
void recvWithStartEndMarkers()
{
     static boolean recvInProgress = false;
     static byte ndx = 0;
     char startMarker = '[';
     char endMarker = ']';
     char rc;
 
     if (HWSERIAL.available() > 0) 
     {
          rc = HWSERIAL.read();
          if (recvInProgress == true) 
          {
               if (rc != endMarker) 
               {
                    receivedChars[ndx] = rc;
                    ndx++;
                    if (ndx > maxDataLength) { ndx = maxDataLength; }
               }
               else 
               {
                     receivedChars[ndx] = '\0'; // terminate the string
                     recvInProgress = false;
                     ndx = 0;
                     newData = true;
               }
          }
          else if (rc == startMarker) { recvInProgress = true; }
     }
}
 
do
Serial.println(EEPROM.read(1));
in setup()
print the same value as the one in
processCommand() ?

also you don't have
Serial.println(freq);

in the setup()

how do you then know if the freq is wrong?
 
Your right...but I have connected an amplifier and loudspeaker to the audio board and so I can hear that is not the same frequency before reboot.
 
Yes but actually printing the values at boot would help both you and us to pinpoint the problem much easier
both formulas are the same, so in theory it should work, can not see any direct problems.

I did now test your code with printing the freq.
and after restart the value read from the eeprom gives the same calculated freq.
so there it at least not any problem there.

When you are testing the code are you using the switch command?
And what do you have connected to the switch inputs (but you use them as outputs)

Code:
pinMode(SWITCH_PIN[pin], INPUT);  // here they are set as input
digitalWrite(SWITCH_PIN[pin],LOW); // here they will become outputs
 
First of all, MANY THANKS for your support!

Before reboot:
Value Slider V = 222
EEPROM-Wert = 222
vol = 222
Value Slider F = 125
EEPROM-Wert = 125
adj = 125
frequency = 402.08

reboot:
vol in setup section = 222
adj in setup section = 125
frequency in setup = 402.08


I was able to localize the problem. If I change the frequency only with the slider and reboot, then it fits.
But if I press a switch e.g. low-pass and now change the frequency, then I get the same value displayed as frequency, but the sound via speaker is not the same as before the reboot.
If I press the same switch again (e.g. low-pass), then I have the same sound via speaker.

How can I fix the problem -> also save the switch status in the EEPROM and write it to the setup?
 
do you have physical switches connected?
then you should not do any of the writes

Code:
digitalWrite(SWITCH_PIN[0],HIGH);
 digitalWrite(SWITCH_PIN[1],HIGH);
 digitalWrite(SWITCH_PIN[2],HIGH);

but maybe you have LED:s connected?

you could mix physical with logical switches
but then you need to make sure that the physical switches only
apply when pressed, i.e. reading them and only applying
when they are at the pressed state

if (digitalRead(SWITCH_PIN[0]) == (HIGH/LOW) {

}

How can I fix the problem
also save the switch status in the EEPROM and write it to the setup?
yes
 
There are three switches and three sliders on the mobile app
I do not have physical switches or sliders /potentiometers.
A new problem:
also the order when which switch (low-, band-, high-pass) is pressed is important, otherwise after rebooting the sound via speaker is not the same :-(
It is only important that pressing e.g. S01=ON or S11=ON or S21=ON" is remembered.
The other state S00=OFF, S10=OFF, S20=OFF seems to have no influence on the settings.
 
here is what you can do

Code:
#define EEPROM_FILTER_STATES_ADDRESS 2

typedef struct {                              
  byte lp;
  byte bp;
  byte hp;
}FilterStatesType;

FilterStatesType filterStates = {0,0,0};

void applyFilterState() {
    mixer1.gain(0, (float)filterStates.lp);  
    mixer1.gain(1, (float)filterStates.bp);
    mixer1.gain(2, (float)filterStates.hp);
    mixer2.gain(0, (float)filterStates.lp);  
    mixer2.gain(1, (float)filterStates.bp);
    mixer2.gain(2, (float)filterStates.hp);
}
void setFilterState(byte num) {
    if  (num == 0) {
        Serial.println("Low Pass Signal");
        filterStates.lp = 1; // hear low-pass signal
        filterStates.bp = 0;
        filterStates.hp = 0;
    }
    else if  (num == 1) {
        Serial.println("Band Pass Signal");
        filterStates.lp = 0;
        filterStates.bp = 1; // hear band-pass signal
        filterStates.hp = 0;
    }
    else if  (num == 2) {
        Serial.println("High Pass Signal");
        filterStates.lp = 0;
        filterStates.bp = 0;
        filterStates.hp = 1; // hear high-pass signal
    }
    else
        return; // nothing changed, just return
	
    applyFilterState();
    EEPROM.put(EEPROM_FILTER_STATES_ADDRESS, filterStates);
}
// or maybe this is what you want?
void setFilterState(byte num, byte state) {
	
    if  (num == 0) {
        Serial.println("Low Pass Signal");
        filterStates.lp = state;
    }
    else if  (num == 1) {
        Serial.println("Band Pass Signal");
        filterStates.bp = state;
    }
    else if  (num == 2) {
		Serial.println("High Pass Signal");
        filterStates.hp = state;
    }
    else
        return; // nothing changed, just return
	
    applyFilterState();
    EEPROM.put(EEPROM_FILTER_STATES_ADDRESS, filterStates);
}


void processCommand(){

    Serial.print("receivedChars = ");   Serial.println(receivedChars);
     
    if (receivedChars[0] == 'S')      // do we have a SWITCH command?
    {
        byte num = receivedChars[1] - 48;          // convert ascii to value by subtracting 48
        if (receivedChars[2] == 0x00) { // one parameter command
            setFilterState(num);
        }
        else { // two parameters command
            byte state = receivedChars[2] - 48;
            setFilterState(num,state); 
        }
    }
    // other code here ....    
}

void setup() {
	// load and apply filter state settings
	EEPROM.get(EEPROM_FILTER_STATES_ADDRESS, filterStates);
	applyFilterState();
}

but I don't understand when the order of the switches would matter
as you actually set all the gain:s at every state-change

also I did notice that you set the gains with the volume command as well
how do you want that to work?

I can see two scenarios

1. do it like you do now,
'buttons'-filterState command overriding prev. volume setting
volume command overriding filterState

2. having the 'switches' decide which gains to be set with the volume setting
i.e. when a filterState is set to one then the gain would apply
and when a filterState is set to zero then that specific gain would still be at zero
 
Last edited:
note. that the EEPROM in teensy 3.2 is not emulated
that means only write settings that change,
to avoid unnecessary tear

don't understand the numbers in the datasheet, but they seems to be very low
 
Thank you so much for your support - You are the best!

I'm so sorry, but I'm a complete newbie with the Teensy + Audio Board. (I'm sure you've noticed it already)

That's what I want:
- a slider for volume control
- a slider for mixing the input signals (white and pink noise)
- a slider for the frequency control of the three filters
- low-pass-filter
- band-bass-filter
- high-pass-filter

And if the power fails, all values should be stored and the Teensy should reboot with the last valid settings.

I can also use Teensy 4.0 + Audio Board if this is the better "team"
 
Last edited:
I have tried your code, but I get an error.....

In this part is the error:

Code:
void setFilterState(byte num, byte state) {
  
    if  (num == 0) {
        Serial.println("Low Pass Signal");
        filterStates.lp = state;
    }
    else if  (num == 1) {
        Serial.println("Band Pass Signal");
        filterStates.bp = state;
    }
    else if  (num == 2) {
    Serial.println("High Pass Signal");
        filterStates.hp = state;
    }
    else
        return; // nothing changed, just return
  
    applyFilterState();
    EEPROM.put(EEPROM_FILTER_STATES_ADDRESS, filterStates);
}

void processCommand(){

    Serial.print("receivedChars = ");   Serial.println(receivedChars);
     
    if (receivedChars[0] == 'S')      // do we have a SWITCH command?
    {
        byte num = receivedChars[1] - 48;          // convert ascii to value by subtracting 48
        if (receivedChars[2] == 0x00) { // one parameter command
            setFilterState(num);
        }
        else { // two parameters command
            byte state = receivedChars[2] - 48;
            setFilterState(num,state); 
        }
    }

Error message: error: -> too few arguments to function 'void setFilterState(byte, byte)' setFilterState(num);
note: -> declared here -> void setFilterState(byte num, byte state) {
 
you will need the other function as well
void setFilterState(byte num)
that was in my example @ post #10
If you want to both have the possibility
to send a S command

[Sn] where n is the state

and

[Sns] as before but also make it possible to turn on/off a specific filter with either having s 0 or 1

example if you have
buttons like this
buttons.png
 
You are simply the BEST!!!
Thank you so much for your support!!!
Now I will drink a GinTonic on your health :)
 
Last edited:
Back
Top