Control-Surface icon indicating copy to clipboard operation
Control-Surface copied to clipboard

2 different Banks saving data on SD with Json Structure

Open danilobelpedio opened this issue 2 years ago • 3 comments

First of everything, Pieter thanks for your wonderful job. I Am Danilo from Italy and I have to ask you help for an ideas that arrived from problems during music live performance . I am not a musician (hobby), I am not a programmer (hobby), and I have few times to do a lot of thing that I have in my head. I programmed at university and sometimes at work but when I programmed first time there was the "basic" program language on so I m new in this kind of wonderful system like Arduino. Well, it's the first time I write here and I solved a lot of problems reading here, so thanks also to everyone. My problem is: how I can use two different banks (like bank1 and bank2) for two arrays of encoders saving data on a json structure and alter on a sd?

My code is a mix of your code and other code around the network... I modified also your code (sorry don't be angry and I hope that I done good job...)

Anyways the code below is only for one encoder one bank: (I USE an ARDUINO MEGA... because the code need more memory )

#include <Control_Surface.h> // Include the Control Surface library
#include <SPI.h> 
#include <ArduinoJson.h>
#include <SD.h>

#define SS 53     //Pin del SS del SD Reader

// variabile per la definizione del bottone di write e read
#define write 3          //Pin del pulsante per scrivere i data su SD
#define read 2          //Pin del pulsante per leggere i data salvati su SD

// definisco i Pin del RGBLed per la SD
#define RgbLedRedPin 54   //A0 analogPin
#define RgbLedGreenPin 55 //A1 analogPin
#define RgbLedBluePin 56  //A2 analogPin

//variabili per il debounce dei pulsanti
long Debounce_delay = 100;
long t1 = 0;

const char *filename = "MONITORS.TXT";  // <- SD library uses 8.3 filenames

// Creo l'oggetto JSON 
DynamicJsonDocument doc(1024);
JsonObject obj;

File myFileSDCart;

JsonObject getJSonFromFile(DynamicJsonDocument *doc, String filename, bool forceCleanONJsonError = true ) 
  {
    myFileSDCart = SD.open(filename);     // open the file for reading:
    if (myFileSDCart) {                   // read from the file until there's nothing else in it:
        DeserializationError error = deserializeJson(*doc, myFileSDCart);
        if (error) { 
            Serial.print(F("Error parsing JSON ")); // if the file didn't open, print an error:
            Serial.println(error.c_str());
            if (forceCleanONJsonError){
                return doc->to<JsonObject>();
            }
        }
        myFileSDCart.close();     // close the file:
        return doc->as<JsonObject>();
    } else {
        Serial.print(F("Error opening (or file not exists) "));   // if the file didn't open, print an error:
        Serial.println(filename);
        Serial.println(F("Empty json created"));
        return doc->to<JsonObject>();
    }
}

// Custom callback to handle output for a selector.
class MySelectorCallback {
  uint8_t pickedColor[10][3] ={ // defined Bank Colors R,G,B
    {255,  0,  0},     //rosso
    {200,100, 53},     //giallo limone
    {  0,  0,150},     //blu
    {  0,100,  0},     //verde
    {255,  0,255},     //magenta
    { 10, 50,255},     //azzurro
    {255,255,255},     //bianco
    {100,100,255},     //celeste
    {  0,  0,  0},      //SPENTO
    {128,  0,128},     //viola
  };

  public:
    // Constructor
    MySelectorCallback(pin_t redLED, pin_t greenLED, pin_t blueLED)
      : redLED(redLED), greenLED(greenLED), blueLED(blueLED) {}

    // Begin function is called once by Control Surface.
    // Use it to initialize everything.
    void begin() { //PinMode deleted because are AnalogPin not Digital
      show(0);
    }

    // Update function is called continuously by Control Surface.
    // Use it to implement things like fading, blinking ...
    void update() {}

    // Update function with arguments is called when the setting
    // changes.
    // Use it to update the LEDs.
    void update(setting_t oldSetting, setting_t newSetting) {
      (void) oldSetting; // unused in this example
      show(newSetting);
    }

  private:
    // Show the color of the given setting.
    void show(setting_t setting) {
      uint8_t colorRed = getColorRed(setting);
      uint8_t colorGreen = getColorGreen(setting);
      uint8_t colorBlue = getColorBlue(setting);
      analogWrite(redLED, colorRed); //if were digital pin i should use DigitalWrite 
      analogWrite(greenLED, colorGreen);
      analogWrite(blueLED, colorBlue);
    }

    // Convert the given setting to a 3-bit RGB color value.
    uint8_t getColorRed(setting_t setting) {
      switch (setting) {
        case 0: return pickedColor[setting][0];
        case 1: return pickedColor[setting][0];
        case 2: return pickedColor[setting][0];
        case 3: return pickedColor[setting][0];
        case 4: return pickedColor[setting][0];
        case 5: return pickedColor[setting][0];
        case 6: return pickedColor[setting][0];
        case 7: return pickedColor[setting][0];
//        case 8: return pickedColor[setting][0];
        default: return 0;
      }
    }
     uint8_t getColorGreen(setting_t setting) {
      switch (setting) {
        case 0: return pickedColor[setting][1];
        case 1: return pickedColor[setting][1];
        case 2: return pickedColor[setting][1];
        case 3: return pickedColor[setting][1];
        case 4: return pickedColor[setting][1];
        case 5: return pickedColor[setting][1];
        case 6: return pickedColor[setting][1];
        case 7: return pickedColor[setting][1];
//        case 8: return pickedColor[setting][1];
        default: return 0;
      }
    }
    uint8_t getColorBlue(setting_t setting) {
      switch (setting) {
        case 0: return pickedColor[setting][2];
        case 1: return pickedColor[setting][2];
        case 2: return pickedColor[setting][2];
        case 3: return pickedColor[setting][2];
        case 4: return pickedColor[setting][2];
        case 5: return pickedColor[setting][2];
        case 6: return pickedColor[setting][2];
        case 7: return pickedColor[setting][2];
//        case 8: return pickedColor[setting][2];
        default: return 0;
      }
    }


  private:
    // Member variables to remember the pin numbers of the LEDs.
    pin_t redLED, greenLED, blueLED;
};

// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //


//USBMIDI_Interface midi;
USBDebugMIDI_Interface midi;

// Create 8 banks with one track per bank.
Bank<8> bank(1); // 8 musicians, 1 track (encoder) for each musician

// Create a selector that uses our custom callback, to control the bank.
GenericIncrementDecrementSelector<8, MySelectorCallback> selector {
  bank,         // bank to manage
  {5, 6, 7}, // red, green, blue LED pins 
                // (this is the MySelectorCallback constructor defined above)
  {8, 9},       // incr/decr button pins
  Wrap::Wrap,   // wrap around when reaching setting 6
};

// Handy type alias
using  CCAbsEnc = Bankable::CCAbsoluteEncoder<8>;

// Instantiate a CCAbsoluteEncoder object
CCAbsEnc enc[] = {
  {  
  {bank, BankType::CHANGE_ADDRESS},
  {22, 23},       // pins
  {7,CHANNEL_5}, // MIDI address (CC number + optional channel)
  1,            // optional multiplier if the control isn't fast enough
  },
};

void setup() {
  pinMode(RgbLedRedPin, OUTPUT);
  pinMode(RgbLedGreenPin, OUTPUT);
  pinMode(RgbLedBluePin, OUTPUT);
  pinMode(write, INPUT_PULLUP);        // Inizializiamo il pin del pulsante di write
  pinMode(read, INPUT_PULLUP);        // Inizializiamo il pin del pulsante di read
  Control_Surface.begin(); // Initialize Control Surface
  Serial.begin(115200);
  while (!SD.begin(SS)) {   // Initialize SD library
        Serial.println("Failed");
        Serial.println("to initialize");
        Serial.println("SD library");
        for (int i =0; i<4; i++){
          BlinkSdLed(0);
          delay(200);
          BlinkSdLed(1);
          delay(200);
          BlinkSdLed(2);
          delay(200);
        }
        BlinkSdLed(0);
    }

  Serial.println("SD library");
  Serial.println("initialized");
  Serial.println("READY TO WORK!");
  BlinkSdLed(0);
  delay(300);
  BlinkSdLed(1);
  delay(300);
  BlinkSdLed(2);
  delay(300);
  BlinkSdLed(1); 
  readData();
}

void loop() {
  Control_Surface.loop(); // Update the Control Surface
  readFromSD();
  writeOnSD();
}

void BlinkSdLed(int color)
{
  switch(color) {
  case 0:
    digitalWrite(RgbLedRedPin,255);
    digitalWrite(RgbLedGreenPin,0);
    digitalWrite(RgbLedBluePin,0);
  break;
  case 1:
    digitalWrite(RgbLedRedPin,0);
    digitalWrite(RgbLedGreenPin,255);
    digitalWrite(RgbLedBluePin,0);
  break;
    case 2:
    digitalWrite(RgbLedRedPin,0);
    digitalWrite(RgbLedGreenPin,0);
    digitalWrite(RgbLedBluePin,255);
  break;
  }
}

void writeOnSD(){
    if (millis() - t1 > Debounce_delay){                           
    if (digitalRead(write)==LOW) {                                                                      
    obj = getJSonFromFile(&doc, filename);                        
    for (int j = 0; j < 8; j++) {                          
    obj[F("encVals")][j] = enc[0].getValue(j);                            
      }                                                               
    t1 = millis();                                               
    boolean isSaved = saveJSonToAFile(&doc, filename);            
    if (isSaved){
        BlinkSdLed(0);                                                  
        Serial.println("File saved!");
        delay(300);
        BlinkSdLed(1);                                                                                
    }else{
        BlinkSdLed(0);                                                        
        Serial.println("Error on save File!");
        for (int i =0; i<4; i++){
          BlinkSdLed(1);
          delay(200);
          BlinkSdLed(2);
          delay(200);
          BlinkSdLed(0);
          delay(200);
        }
      BlinkSdLed(1);                                                                     
    }    
    }
  }     
}

void readData(){
    obj = getJSonFromFile(&doc, filename);  // riempio l'array dei valori salvati e letti da SD attraverso la struttura Json
  for (int j=0; j < 8; j++) {
      enc[0].setValue(obj[F("encVals")][j],j);
    }
}

void readFromSD(){
  if (millis() - t1 > Debounce_delay){ 
    if (digitalRead(read)==LOW) {
      BlinkSdLed(0);                                                  
      Serial.println("READ DATA...!");
      delay(300);
      BlinkSdLed(1);
      t1 = millis();
      readData();
    }
  }
}

bool saveJSonToAFile(DynamicJsonDocument *doc, String filename) {
    SD.remove(filename);
    myFileSDCart = SD.open(filename, FILE_WRITE); // open the file. note that only one file can be open at a time, so you have to close this one before opening another.
    if (myFileSDCart) {
        serializeJson(*doc, myFileSDCart);
        myFileSDCart.close();   // close the file:
        return true;
    } else {        
        return false;
    }
}

Everything work good in this situation... the Jason structure is something like that: {encVals:{64,64,64,64,64,64,64,64}}

where I save on a file called MONITORS.TXT this structure and I call it every time I need (with a button) (not yet implemented but already ready because used in a previous release of this project) and when I turn on Arduino. 8 values are the 8 banks for 1 encoder... so I need a structure with 11 encoders, so 88 of that structure values... so ... with the code of

  for (int j=0; j < 8; j++) {
      enc[0].setValue(obj[F("encVals")][j],j);
    }

I read values from the json structure

with this code

    for (int j = 0; j < 8; j++) {                          
    obj[F("encVals")][j] = enc[0].getValue(j);                            
      }      

I write values in the json structure later I write date on the SD serializing and deserializing the structure on the SD... Ff course if I need for 11 encoders I need to do another for cycle to from 0 to 11 to save the other encoders value.

well now I have connected at the same Mega other 8 encoders with 9 banks so I will need something like that

Bank<9> bank(8); 

// Create a selector that uses our custom callback, to control the bank.
GenericIncrementDecrementSelector<8, MySelectorCallback> selector1 {
  bank1,         // bank to manage
  ......
};

// Handy type alias
using  CCAbsEnc1 = Bankable::CCAbsoluteEncoder<9>; 

CCAbsEnc1 enc1[] = {
  {  
  {bank1, BankType::CHANGE_ADDRESS},
  .....
};

probably I need something like that but my problem is

  for (int j=0; j < 8; j++) {
      enc[0].setValue(obj[F("encVals")][j],j);
    }

I read values from the json structure

with this code

    for (int j = 0; j < 8; j++) {                          
    obj[F("encVals")][j] = enc[0].getValue(j);                            
      }  

how I can change this code to identify the two different banks?

Thanks for the help... thanks a lot

danilobelpedio avatar Feb 02 '23 10:02 danilobelpedio

how I can change this code to identify the two different banks?

Which two banks?

JSON sounds like overkill for this task, to be honest. You could consider using the internal EEPROM instead.
As a side note, you can get rid of the switch statements for the color selection, since each case is the same.

tttapa avatar Feb 02 '23 14:02 tttapa

Thanks for the answer Pieter, I mean that i need something like that:

Bank<8> bankMonitors(11); // 8 banks for 11 encoders Bank<9> bankMixer(8); // 9 banks for 8 encoders

I used json but of course could be better to write on the EEPROM... I had problems with the SD library because in that library you write on the SD only chars not integer and i didn't find a good solutions to convert datas...so i tryed json an solved... Anyways what you means that i can remove the switch statment to pick the color? Because i need 3 different colors taken from the array...but I don't understand what you mean about that are the same... Thanks a lot for your help!

danilobelpedio avatar Feb 02 '23 17:02 danilobelpedio

Bank<8> bankMonitors(11); // 8 banks for 11 encoders Bank<9> bankMixer(8); // 9 banks for 8 encoders

It's not clear to me where you're stuck exactly. Is it the data format to save? How to get the current settings out of the Control Surface object? How to set them again when loading from the SD card?

I had problems with the SD library because in that library you write on the SD only chars not integer and i didn't find a good solutions to convert datas...so i tryed json an solved...

Internally, the Arduino represents all data as 8-bit bytes. Your storage medium doesn't need to have the ability to store arbitrary types, if it also supports 8-bit bytes, you can store anything.

Anyways what you means that i can remove the switch statment to pick the color? Because i need 3 different colors taken from the array...but I don't understand what you mean about that are the same...

    uint8_t getColorRed(setting_t setting) {
        if (setting >= 8)
            return 0;
        return pickedColor[setting][0];
    }

You also don't need a function for each color, but let's not get ahead of ourselves.

tttapa avatar Feb 04 '23 14:02 tttapa