Go Down

Topic: Need help writing and reading array of CC values into EEPROM (Read 465 times) previous topic - next topic

coryoryoh

Hello,

I have a midi controller with 12 pots sending serial MIDI CC messages to an external digital synth. The ultimate goal is to be able to save and recall preset CC values back into the array of pots the way that you would expect a synthesizer to save and load presets, with the loaded preset values overriding the pot values until that pot is turned with a threshold of 4, etc.

The part I'm stuck at is saving to multiple EEPROM addresses from an array. You'll notice that the first code posted below shows an array of 2 pots as I'm teaching myself how to write/update/read on a separate microcontroller using fewer pots first.

My method of testing has been first using this code to EEPROM.update for the potVal with a momentary button, then reading the address using a modified version of the EEPROM.read example code in the Arduino IDE. My version just counts between the addresses currently being used. When reading the updated potVal with one pot (hence, one address. Not an array), it works great.

When using EEPROM.update with the array that I have made, then uploading the EEPROM.read code, only the value at address 2 appears to have saved. This EEPROM.read code seems to work reliably but I've posted it second just in case.

To sum up my question: What am I doing wrong? Inversely, how do I assign an EEPROM address to each value in an array?

The code is probably atrocious to the experienced eye. Feel free to tear me apart  :D. Learning on my own so beware of bad bracket placement and funky indentations. Thanks!


Code #1: Unsuccessful EEPROM.update Array

Code: [Select]
#include <MIDI.h>
#include <EEPROM.h>

int addr[] = {1, 2};
int potPin[] = {A0, A1};
int controller[] = {1, 2};

int potVal[2];
int lastPotVal[2];
int buttonState = 0;
int lastButtonState = 0;

MIDI_CREATE_DEFAULT_INSTANCE();

void setup() {
MIDI.begin();
Serial.begin(9600); //Not sure if this is redundant.
pinMode(0, INPUT_PULLUP);
pinMode(13, OUTPUT);
}

void loop() {
  for (uint8_t i=0; i<2; i++){
potVal[i] = map(analogRead(potPin[i]), 0, 1023, 0, 127);
if(potVal[i] != lastPotVal[i]){
  lastPotVal[i] = potVal;
  lastPotVal[i] = map(analogRead(potPin[i]), 0, 1023, 0, 127);
  MIDI.sendControlChange(controller[i], potVal[i], 1); //Unrelated, but Suboptimal array?
}
buttonState = digitalRead(0);
  if (buttonState != lastButtonState) {
    if(buttonState == LOW) {
    EEPROM.update(addr[i], potVal[i]);     
      digitalWrite(13, HIGH);
  }
  else{
  digitalWrite(13, LOW);
  }
  delay(50);
  lastButtonState = buttonState;
}

    while (MIDI.read());
      Serial.print("current value will save to addr: ");
      Serial.println(addr[i]);
      Serial.print("CC value: ");
      Serial.println(potVal[i]);
      if (buttonState == LOW){
      Serial.println("saved!"); //if I add a delay here, the button must be pushed for
      }                         //the duration of the delay in order to EEPROM.update.
    }                           //And I don't like it so I just have to read fast.
  }


Code #2: Modified EEPROM.read Example Code, just in case.
Code: [Select]
<#include <EEPROM.h>
int addr[] = {1, 2};
int midiCC[2];
void setup() {
  Serial.begin(9600);
  while (!Serial) {
  }
}
void loop() {
  for (int i = 0; i < 2; i++){
 midiCC[i]  = EEPROM.read(addr[i]);
  }
 for (int i = 0; i < 2; i++){
  Serial.print(addr[i]);
  Serial.print("\t");
  Serial.print(midiCC[i], DEC);
  Serial.println();
 }
  delay(500);
}

cattledog

I assume you are on an 8 bit Arduino where an integer is 2 bytes.

Code: [Select]
int potVal[2];

Code: [Select]
int addr[] = {1, 2};
Each eeprom address can only hold one byte.

You declare potVal as an int, and even though you map it to a smaller value with
Code: [Select]
potVal[i] = map(analogRead(potPin[i]), 0, 1023, 0, 127);
 its value is still allocated across two bytes even though one of them is 0. You can not stuff two bytes into a one byte address

Since you are only using the mapped values from 0 - 127, I would change you code so that potVal is declared as a byte.

A couple of other issues.

Pin 0 is Serial RX, and it is not good to use for a button. You should change it to some other pin.

Your handling(updating) of the lastPotValue should be a simple assignment and not an additional reading.

The entire organization with the button press reading inside of a for loop and then  the Serial print section is not optimal. My recommendation is to work out the eeprom reading and writing first, and then see about reorganizing the program.

What is this line for
Code: [Select]
while (MIDI.read());

Code: [Select]

#include <MIDI.h>
#include <EEPROM.h>

int addr[] = {1, 2};
int potPin[] = {A0, A1};
int controller[] = {1, 2};

//int potVal[2];
byte potVal[2]
//int lastPotVal[2];
byte lastPotVal[2];
int buttonState = 0;
int lastButtonState = 0;

MIDI_CREATE_DEFAULT_INSTANCE();

void setup() {
  MIDI.begin();
  Serial.begin(9600); //Not sure if this is redundant.
  pinMode(0, INPUT_PULLUP);
  pinMode(13, OUTPUT);
}

void loop() {
  for (uint8_t i = 0; i < 2; i++) {
    potVal[i] = map(analogRead(potPin[i]), 0, 1023, 0, 127);
    if (potVal[i] != lastPotVal[i]) {
      lastPotVal[i] = potVal[i];
      //lastPotVal[i] = map(analogRead(potPin[i]), 0, 1023, 0, 127);
      MIDI.sendControlChange(controller[i], potVal[i], 1); //Unrelated, but Suboptimal array?
    }
    buttonState = digitalRead(0);
    if (buttonState != lastButtonState) {
      if (buttonState == LOW) {
        EEPROM.update(addr[i], potVal[i]);
        digitalWrite(13, HIGH);
      }
      else {
        digitalWrite(13, LOW);
      }
      delay(50);
      lastButtonState = buttonState;
    }

    while (MIDI.read());
    Serial.print("current value will save to addr: ");
    Serial.println(addr[i]);
    Serial.print("CC value: ");
    Serial.println(potVal[i]);
    if (buttonState == LOW) {
      Serial.println("saved!"); //if I add a delay here, the button must be pushed for
    }                         //the duration of the delay in order to EEPROM.update.
  }                           //And I don't like it so I just have to read fast.
}



coryoryoh

Thanks, it works! Yeah a lot of the code is borrowing from other open source code, since this is phase 2 of my first ever programming project. 

Just out of curiosity. I see what wrong by mapping the potVal incorrectly, but could you elaborate how an integer is 2 bytes on Arduino, and how that's different than another device/platform? Doesn't a byte's decimal value go up to 255? The part where I'm most confused is how you set it as a byte here and it works fine, though I might just be reading this incorrectly. Every time I'm introduced to a new concept there's 30 more things to learn!

cattledog

Here's a link to the Arduino reference section on "Integer"

https://www.arduino.cc/reference/en/language/variables/data-types/int/

Quote
but could you elaborate how an integer is 2 bytes on Arduino, and how that's different than another device/platform?
There are many microprocessors (e.g. esp8266, esp32,  many Teensy platforms) which have a 32 bit, or 4 byte integer as the generic length.

If you want to store the pot value as an integer, its best to use EEPROM.put() and EEPROM.get() and store the values two addresses apart to give room for the two bytes.

https://www.arduino.cc/en/Reference/EEPROM


coryoryoh

Thank you for that information! 

The one other thing I'm trying to figure out is what type of command I should give if I wanted to be able to press a 'load' button to EEPROM.get a preset while the loop is going. So I press button 1 to save, mess around with the knobs a bit, then press button 2 to load, to bring up the MIDI CC values that I'd just saved. Then, only the knobs that I turn will change from the loaded preset values. Standard stuff in the context of other synths. Almost overwhelming for me.

I attempted to do this using an if statement, where pressing the load button loaded the number into a separate MIDI.sendControlChange, replacing potVal with the new value potValLoad, then making the 'else' the normal CC setup but it did not work at all. Sorry for not having the code. Just a point in the direction of what sort of commands are required for loading and staying there til the pots change would be a big help.

cattledog

EEPROM.put() and EEPROM.get() are the commands for saving and retrieving data, perhaps an array of values.

If you only need a single byte, you can use EEPROM.write() and EEPROM.read().

Placing a value retrieved from eeprom memory into a midi command should be straightforward. I'm not very familiar with the MIDI library, but from what I see for the function you mentioned, it looks like you would use the value read from eeprom for the inControlValue.

Code: [Select]
sendControlChange(DataByte inControlNumber, DataByte inControlValue, Channel inChannel);


Quote
I attempted to do this using an if statement, where pressing the load button loaded the number into a separate MIDI.sendControlChange, replacing potVal with the new value potValLoad, then making the 'else' the normal CC setup but it did not work at all.
You will have to give your best effort at coding, explain what is correct and what fails, and I'll take a look. Doing this without specific code to look at will not be possible.

coryoryoh

I am back. It's the same project but it's not currently in "MIDI" form, so if I should open up a new thread elsewhere, let me know and I will. 
The goal is to press the button and have int EEPROMInit = potVal until the analogRead value is changed by a threshold of 4.
The code below is a mess, and that's because I've been trying so many different things, and the current behavior is very close to the desired behavior. While I fear that my notes are confusing, I wanted to show the work that I had.
 so here are some notes:
- int EEPROMInit as it is now is a meaningless variable. It works just as well with "potVal = EEPROM..." I have it there just in case this is somehow necessary to achieve the goal
- the entire button press situation between if (buttonState... and else{EEPROM... is doesn't work, and needs to be fixed, but there is one thing it's currently doing right, if only for the sake of showing what I'm trying to achieve which is mentioned in the note below.
- The way that potVal behaves in the presence of the command else{EEPROM...  is exactly what I want it to do *when* the button is pressed (the value is EEPROMInit until I change the knob by a threshold of 4, and continues to display the value of digitalRead) BUT only if the section mentioned in the note directly above is there (not "//'d" out of the code), otherwise...
- By removing everything between if (buttonState... and else{EEPROM..., potVal reverts to EEPROMInit while the pot is not being turned. So if the value at that EEPROM address is 34, the serial monitor will read 34 whenever the pot isn't turned. I know why this is happening (because my code is telling it basically if potVal != lastPotVal, EEPROMInit = potVal). I don't see how the presence of the if statement above is 'blocking' it from looping back to EEPROM.read.
Code: [Select]
#include <EEPROM.h>

int buttonState = 0;
int lastButtonState = 0;
int lastInitPot = 0;
int potVal = 0;
int EEPROMInit = 0;
int threshold = 4;

void setup() {
pinMode(1, INPUT_PULLUP);
pinMode(13, OUTPUT);
Serial.begin(9600);
  while (!Serial) {
  }
}

void loop() {

//if(potVal != initPot){
        int initPot = analogRead(A0);
  if (abs(initPot - lastInitPot) > threshold){
  lastInitPot = initPot;
  potVal = initPot;
  }else{ 
  buttonState = digitalRead(1);     
    if (buttonState != lastButtonState){
      if(buttonState == LOW){
      potVal = EEPROM.read(10);
    }else{
      EEPROMInit = EEPROM.read(10);   
      potVal = EEPROMInit;           
      lastButtonState = buttonState; 
      }                               
    }                                 
      delay(50);
  }     
Serial.println(potVal, DEC);
delay(1000);
}


Why won't the forum let me format this comment into paragraphs?

cattledog

Quote
the entire button press situation between if (buttonState... and else{EEPROM... is doesn't work, and needs to be fixed
Code: [Select]
pinMode(1, INPUT_PULLUP);
buttonState = digitalRead(1);


I told you previously to move the button somewhere else. Pin 1 is a serial pin and you can not use it for the button since you are using Serial in your code.

Do that and report back.

cattledog

Your button reading code is bad. You assign buttonState to lastButtonState in the wrong place, and the if else gives you an eeprom read on a button press and release. It should be more like this

Code: [Select]
lastButtonState = buttonState;// assign last reading value to previous read
  buttonState = digitalRead(some pin other than 1);
  if (buttonState != lastButtonState) {
    if (buttonState == LOW) {
      potVal = EEPROM.read(10);
    }
  }
 

coryoryoh

Quote
Pin 0 is Serial RX, and it is not good to use for a button. You should change it to some other pin.
You said pin 0! ;) To be honest, I knew that 1 was tx, but not that it sends serial data when serial is enabled. That's why the program wasn't uploading half the time. 
I finally understand what int=2 bytes means. In my head, an int and a byte were the same thing. Oops.
Assuming that the loaded values work as midi messages, I'm almost done. Tried to not return to you unless I was absolutely stumped. As it goes, the thing that has stumped me seems to be relatively simple. 
I need help returning my array counter to the original value after a certain limit. So (10, 11, 12) turns to (13, 14, 15), and then goes back to (10, 11, 12). Ultimately, the counter will go higher than an increment of 2 just in case there's a difference, so addr would not reset until after a larger number like (22, 23, 24) then back to (10, 11, 12).
The upper command works as intended (counts up by 3 to infinity), but the bottom just skips the value 15 and then begins behaving incorrectly.
Code: [Select]
      addr[i] = addr[i]+3;
      if(i == 15) i=10;


Secondly, I would like another button which skips addresses by a larger 'set' increment. Both buttons simply change the address, but the first button is meant to cycle through "presets" (preset 1 = addr 10 - 12, preset 2 = addr 13 - 15), etc.), and button 2 is "banks" of presets (bank 1 = presets 1-3, bank 2 = presets 4-6, etc.). I would like to begin at the first 'preset' of every 'bank' when the bank button is pressed. For example, if I'm at bank 1, preset (any), pressing the bank button will advance me to bank 2, preset 4 (the first preset of bank 2). 
I may be able to figure part 2 out by myself if I can figure out part 1, but it sounds like I've increased the work by a magnitude by creating a structure and substructure. 

cattledog

Welcome back.

Quote
I need help returning my array counter to the original value after a certain limit. So (10, 11, 12) turns to (13, 14, 15), and then goes back to (10, 11, 12). Ultimately, the counter will go higher than an increment of 2 just in case there's a difference, so addr would not reset until after a larger number like (22, 23, 24) then back to (10, 11, 12).
Code: [Select]
      addr[i] = addr[i]+3;
 if(i == 15) i=10;


I'm not quite understanding, and will need to see complete code, or a minimal example code which demonstrates the problem.

Do you have and array of values and a corresponding array of addresses where they are stored?

coryoryoh

Sorry, I'm complicating things by trying to simplify them.

Quote
Do you have and array of values and a corresponding array of addresses where they are stored?
I think so. Basically everything in this code is an array. Even 'buttonPressed' and 'lastButtonPressed' because before, it would only save or load one address at a time.
Ignore the "bank" concept for now. We'll imagine those don't exist yet. I made a big obnoxious "//" note where the section in question begins and ends.
Code: [Select]

#include <EEPROM.h>

const int  buttonPin1 = 2;
const int  buttonPin2 = 3;         

int potPin[] = {A0, A1, A2};
int buttonState[3];         
int lastButtonState[3];
int buttonState2[3];
int lastButtonState2[3];
int addr[] = {10,11,12};

byte initPot[3];
byte lastInitPot[3];
byte potVal[3];
int threshold = 2;

void setup() {
  pinMode(buttonPin1, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
 
  Serial.begin(9600);
}


void loop() {

  for (int i = 0; i < 3; i++){
  lastButtonState2[i] = buttonState2[i];// assign last reading value to previous read
  buttonState2[i] = digitalRead(buttonPin2);
  if (buttonState2[i] != lastButtonState2[i]) {
    if (buttonState2[i] == LOW) {
      EEPROM.update(addr[i], potVal[i]);
      Serial.println("~~~~Saved!~~~~");
      }
   }
    initPot[i] = analogRead(potPin[i]);
      if (abs(initPot[i] - lastInitPot[i]) > threshold){
        lastInitPot[i] = initPot[i];
          potVal[i] = initPot[i];
      }else{
lastButtonState[i] = buttonState[i]; //~~~~~*****~~~~~*****~~~~~***** THE SECTION IN QUESTION
  buttonState[i] = digitalRead(buttonPin1);
  if (buttonState[i] != lastButtonState[i]) {
    if (buttonState[i] == LOW) {
      potVal[i] = EEPROM.read(addr[i]);
      addr[i] = addr[i]+3; // once this has reached the maximum "preset" number...
      if(i == 15) i=10;    // .WRONG. loop back to preset 1, which is the array of addr(10, 11, 12)
    }                      //~~~~~*****~~~~~*****~~~~~END OF SECTION IN QUESTION
  }             
}
      Serial.print("address");
      Serial.println(addr[i]);
      Serial.print("value");
      Serial.println(potVal[i]);
      delay(500); 
  }

Preset 1 >>button press>> Preset 2 >>button press>> Preset 3 >>button press>> Preset 1...

cattledog

Quote
I need help returning my array counter to the original value after a certain limit. So (10, 11, 12) turns to (13, 14, 15), and then goes back to (10, 11, 12). Ultimately, the counter will go higher than an increment of 2 just in case there's a difference, so addr would not reset until after a larger number like (22, 23, 24) then back to (10, 11, 12).
Code: [Select]
addr[i] = addr[i]+3;
      if(i == 15) i=10;


When you reach this point in the code, i has a value of 0, 1, or 2. 
Code: [Select]
addr[i] will have a value of 10,11,12

I think you want
Code: [Select]
addr[i] = addr[i]+3;
 if(addr[i] == 15) addr[i]=10;


I not quite certain if the reset condition needs to be on == 15. You may not get the reading from 15 if you reset to 10 where you do.

I think the reset may want to be outside of the for loop
Code: [Select]

 }//ending curly brace of for (int i = 0; i < 3; i++)

  if (addr[i] == 15) addr[i] = 10;//maybe reset here??

}//ending curly brace of loop() 

cattledog

Code: [Select]

}//ending curly brace of for (int i = 0; i < 3; i++)

  if (addr[i] == 15) addr[i] = 10;//maybe reset here??

}//ending curly brace of loop()


After some thought, I think the reset to the original address values needs to be for the entire array, and not just one element.


Code: [Select]
}//ending curly brace of for (int i = 0; i < 3; i++)

  for(byte i= 0; j<3; ++)
  {
     addr[i] = 10 + i; //reset array to original values
  }
}//ending curly brace of loop()








































































Go Up