Не записываются измененные данные в EEPROM.

mambo174

✩✩✩✩✩✩✩
9 Окт 2023
7
0
Доброго всем дня. Есть код вывода меню настроек написанный под ESP32+LCD1602 с возможностью изменения параметров с помощью энкодера EC11. Но значения по умолчанию выводит. изменить значение получается, но записать измененное значение не получается. При перезагрузки устройства значения загружаются по умолчанию.
C++:
#include <EEPROM.h>

// пример с оптимизированным линейным меню
// оптимизировано очищение дисплея
// названия засунуты в PROGMEM вместо оперативки
// менять значения можно нажатым поворотом, или КЛИКНУТЬ, курсор изменится, и крутить
// значение меняется быстрее при быстром повороте
#include "pgmspace.h"

#define LINES 2       // количество строк дисплея
#define SETTINGS_AMOUNT 6  // количество настроек
#define FAST_STEP 5   // скорость изменения при быстром повороте


// пины энкодера
#define CLK 25
#define DT 32
#define SW 33


#include "GyverEncoder.h"
Encoder enc1(CLK, DT, SW);  // для работы c кнопкой

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // адрес 0x27 или 0x3f
int8_t arrowPos = 0;
int8_t screenPos = 0; // номер "экрана"
int vals[SETTINGS_AMOUNT];
bool isFirstRun;

//Массив пунктов меню который выводится на экране
const char *settingsNames[]  = {
  "MaxT kotla",
  "MaxT home",
  "Ts1MIN",
  "Ts2MIN",
  "Ts3MIN",
  "ModeA",
};
//Структура хранения данных в EEptrom, рассчитана на 6 пунктов меню 5 из которых имеют тип данных int, 1 пункт тип данных bool
struct MenuItem{
  char name[10];
  int value;
  bool mode;
  };
 
  MenuItem param[6];

void setup(){
  Serial.begin(115200);
  enc1.setType(TYPE2);
  EEPROM.begin(100);
  // if(EEPROM.read(50)!='w'){
  //   EEPROM.put(50,'w');
  //   writeParamStruct(); //При первой компиляции было раскоментировано и данные были записаны в EEPROM
  //   EEPROM.put(0,param);   
  //   EEPROM.commit();
  // }else{
    EEPROM.get(0,param);
    EEPROM.commit();
  // }
  lcd.init();
  lcd.clear();
  lcd.backlight();
  //Читаем данные из EEPROM
  for(int i=0; i<6;i++){
    EEPROM.get(i*sizeof(MenuItem), param[i]);
    EEPROM.commit();
  }
 
    displayMenu();
  }

//Заполнение структры массива данными по умолчанию
void writeParamStruct(){
  sprintf(param[0].name, "TempKMax");
  param[0].value = 80;
 
  sprintf(param[1].name, "TempHMax");
  param[1].value = 25;
 
  sprintf(param[2].name, "TS1MIN");
  param[2].value = 10;
 
  sprintf(param[3].name, "TS2MIN");
  param[3].value = -5;
 
  sprintf(param[4].name, "TS3MIN");
  param[4].value = -15;
 
  sprintf(param[5].name, "AutoMode");
  param[5].mode = true;
 
  for (int i = 0; i < 6; i++) {
    int address = i * sizeof(struct MenuItem);
    EEPROM.put(address, param[i]);
    EEPROM.commit();
  }
    
} 

//Вывод пунктов меню с данными на дисплей 1602
void displayMenu() {
  lcd.clear();
  screenPos = arrowPos / LINES;
  // Отображение текущего пункта меню и его значение на первой строке дисплея

  for (int i = 0; i < LINES; i++) {
    lcd.setCursor(0, i);
    if (arrowPos == LINES * screenPos + i) lcd.write(126);  // рисуем стрелку
    else lcd.write(32);     // рисуем пробел

    // если пункты меню закончились, покидаем цикл for
    if (LINES * screenPos + i == SETTINGS_AMOUNT) break;
    lcd.print(settingsNames[LINES * screenPos+i]);
    lcd.setCursor(12, i);
    lcd.print(param[LINES * screenPos+i].value);
  }
}

void loop(){
  enc1.tick();
  Если было двойное нажатие то записываем данные по умолчанию
   if(enc1.isDouble()){
     writeParamStruct();
     lcd.clear();
     lcd.setCursor(0,0);
     lcd.print("Default");
     delay(5000);
     displayMenu();
   }


  if (enc1.isTurn()){
    //Блок выбора пункта меню
    int increment =0;                         
    if(enc1.isRight()) increment =1;
    if(enc1.isLeft()) increment =-1;
    arrowPos += increment;
    arrowPos = constrain(arrowPos,0,  SETTINGS_AMOUNT -1);

    //Блок изменения значения пункта меню при удержании нажатия и прокручивании энкодера.
    increment = 0;  // обнуляем инкремент
    if (enc1.isRightH()) increment = 1;
    if (enc1.isLeftH()) increment = -1;
    param[arrowPos].value +=increment;

    displayMenu();
  }

  //Запись данных при удержании нажатия энкодера
  if(enc1.isHolded()){
    EEPROM.begin(100);

    EEPROM.put(0,param);
    EEPROM.commit();

    lcd.clear();

    lcd.setCursor(0,0);
    lcd.print("Write");
    delay(5000);
    displayMenu();
   }
  }
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,974
633
45
У есп нет еепром, есть эмуляция во флеш памяти. Не уверен что эта область не затирается при перепрошивке.
 

mambo174

✩✩✩✩✩✩✩
9 Окт 2023
7
0
@Геннадий П, Они не затираются так как пункты меню отображаются со значениями по умолчанию, метод который заполняет структуру массива данные в дальнейшем закомментирован. При этом по двойному нажатию массив спокойно заполняется данными по умолчанию и сохраняется.
 

mambo174

✩✩✩✩✩✩✩
9 Окт 2023
7
0
Разобрался изменил механизм записи данных добавив две строчки. Актуальный код ниже.
C++:
#include <EEPROM.h>

#include "pgmspace.h"

#define LINES 2       // количество строк дисплея
#define SETTINGS_AMOUNT 6  // количество настроек
//#define FAST_STEP 5   // скорость изменения при быстром повороте


// пины энкодера
#define CLK 25
#define DT 32
#define SW 33
int i;

#include "GyverEncoder.h"
Encoder enc1(CLK, DT, SW);  // для работы c кнопкой

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // адрес 0x27 или 0x3f
int8_t arrowPos = 0;
int8_t screenPos = 0; // номер "экрана"
int vals[SETTINGS_AMOUNT];
bool isFirstRun;

//Массив пунктов меню который выводится на экране
const char *settingsNames[]  = {
  "MaxT kotla",
  "MaxT home",
  "Ts1MIN",
  "Ts2MIN",
  "Ts3MIN",
  "ModeA",
};
//Структура хранения данных в EEptrom, рассчитана на 6 пунктов меню 5 из которых имеют тип данных int, 1 пункт тип данных bool
struct MenuItem{
  char name[10];
  int value;
  bool mode;
  };

  MenuItem param[6];

void setup(){
  Serial.begin(115200);
  enc1.setType(TYPE2);
  EEPROM.begin(100);
  // if(EEPROM.read(50)!='w'){
  //   EEPROM.put(50,'w');
  //   writeParamStruct(); //При первой компиляции было раскоментировано и данные были записаны в EEPROM
  //   EEPROM.put(0,param);  
  //   EEPROM.commit();
  // }else{
    EEPROM.get(0,param);
    EEPROM.commit();
  // }
  lcd.init();
  lcd.clear();
  lcd.backlight();
  //Читаем данные из EEPROM
  for(int i=0; i<6;i++){
    EEPROM.get(i*sizeof(MenuItem), param[i]);
    EEPROM.commit();
  }

    displayMenu();
  }

//Заполнение структры массива данными по умолчанию
void writeParamStruct(){
  sprintf(param[0].name, "TempKMax");
  param[0].value = 80;

  sprintf(param[1].name, "TempHMax");
  param[1].value = 25;

  sprintf(param[2].name, "TS1MIN");
  param[2].value = 10;

  sprintf(param[3].name, "TS2MIN");
  param[3].value = -5;

  sprintf(param[4].name, "TS3MIN");
  param[4].value = -15;

  sprintf(param[5].name, "AutoMode");
  param[5].mode = true;

  for (i = 0; i < 6; i++) {
    int address = i * sizeof(struct MenuItem);
    EEPROM.put(address, param[i]);
    EEPROM.commit();
  }
   
}

//Вывод пунктов меню с данными на дисплей 1602
void displayMenu() {
  lcd.clear();
  screenPos = arrowPos / LINES;
  // Отображение текущего пункта меню и его значение на первой строке дисплея

  for (int i = 0; i < LINES; i++) {
    lcd.setCursor(0, i);
    if (arrowPos == LINES * screenPos + i) lcd.write(126);  // рисуем стрелку
    else lcd.write(32);     // рисуем пробел

    // если пункты меню закончились, покидаем цикл for
    if (LINES * screenPos + i == SETTINGS_AMOUNT) break;
    lcd.print(settingsNames[LINES * screenPos+i]);
    lcd.setCursor(12, i);
    lcd.print(param[LINES * screenPos+i].value);
  }
}

void loop(){
  enc1.tick();
  //Если было двойное нажатие то записываем данные по умолчанию
  if(enc1.isDouble()){
    writeParamStruct();
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Default");
    delay(5000);
    displayMenu();
  }


  if (enc1.isTurn()){
    //Блок выбора пункта меню
    int increment =0;                        
    if(enc1.isRight()) increment =1;
    if(enc1.isLeft()) increment =-1;
    arrowPos += increment;
    arrowPos = constrain(arrowPos,0,  SETTINGS_AMOUNT -1);

    //Блок изменения значения пункта меню при удержании нажатия и прокручивании энкодера.
    increment = 0;  // обнуляем инкремент
    if (enc1.isRightH()) increment = 1;
    if (enc1.isLeftH()) increment = -1;
    param[arrowPos].value +=increment;

    displayMenu();
  }

  //Запись данных при удержании нажатия энкодера
  if(enc1.isHolded()){
    EEPROM.begin(100);
    int address = arrowPos * sizeof(struct MenuItem); //Получаем адресс выбранной ячейки
    EEPROM.put(address,param[arrowPos]); //Записывавем по актуальные данные по выбранному меню

    // EEPROM.put(0,param);
    EEPROM.commit();

    lcd.clear();

    lcd.setCursor(0,0);
    lcd.print("Write");
    delay(5000);
    displayMenu();
   }
  }
 

mambo174

✩✩✩✩✩✩✩
9 Окт 2023
7
0
Теперь есть другой момент. Не переписывается значение типа данных boolean. Значение меняется но запись не происходит.
C++:
#include <EEPROM.h>

// пример с оптимизированным линейным меню
// оптимизировано очищение дисплея
// названия засунуты в PROGMEM вместо оперативки
// менять значения можно нажатым поворотом, или КЛИКНУТЬ, курсор изменится, и крутить
// значение меняется быстрее при быстром повороте
#include "pgmspace.h"

#define LINES 2       // количество строк дисплея
#define SETTINGS_AMOUNT 6  // количество настроек
#define FAST_STEP 5   // скорость изменения при быстром повороте


// пины энкодера
#define CLK 25
#define DT 32
#define SW 33
// int i;

#include "GyverEncoder.h"
Encoder enc1(CLK, DT, SW);  // для работы c кнопкой

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // адрес 0x27 или 0x3f
int8_t arrowPos = 0;
int8_t screenPos = 0; // номер "экрана"
int vals[SETTINGS_AMOUNT];
bool isFirstRun;

//Массив пунктов меню который выводится на экране
const char *settingsNames[]  = {
  "MaxT kotla",
  "MaxT home",
  "Ts1MIN",
  "Ts2MIN",
  "Ts3MIN",
  "ModeA",
};
//Структура хранения данных в EEptrom, рассчитана на 6 пунктов меню 5 из которых имеют тип данных int, 1 пункт тип данных bool
struct MenuItem{
  char name[10];
  int value;
  bool mode;
  };
 
  MenuItem param[6];

void setup(){
  Serial.begin(115200);
  enc1.setType(TYPE2);
  EEPROM.begin(100);
  // if(EEPROM.read(50)!='w'){
  //   EEPROM.put(50,'w');
  //   writeParamStruct(); //При первой компиляции было раскоментировано и данные были записаны в EEPROM
  //   EEPROM.put(0,param);   
  //   EEPROM.commit();
  // }else{
    EEPROM.get(0,param);
    EEPROM.commit();
  // }
  lcd.init();
  lcd.clear();
  lcd.backlight();
  //Читаем данные из EEPROM
  for(int i=0; i<6;i++){
    EEPROM.get(i*sizeof(MenuItem), param[i]);
    EEPROM.commit();
  }
 
    displayMenu();
  }

//Заполнение структры массива данными по умолчанию
void writeParamStruct(){
  sprintf(param[0].name, "TempKMax");
  param[0].value = 80;
 
  sprintf(param[1].name, "TempHMax");
  param[1].value = 25;
 
  sprintf(param[2].name, "TS1MIN");
  param[2].value = 10;
 
  sprintf(param[3].name, "TS2MIN");
  param[3].value = -5;
 
  sprintf(param[4].name, "TS3MIN");
  param[4].value = -15;
 
  sprintf(param[5].name, "AutoMode");
  param[5].mode = true;
 
  for (int i = 0; i < 6; i++) {
    int address = i * sizeof(struct MenuItem);
    EEPROM.put(address, param[i]);
    EEPROM.commit();
  }
    
} 

//Вывод пунктов меню с данными на дисплей 1602
void displayMenu() {
  lcd.clear();
  screenPos = arrowPos / LINES;
  // Отображение текущего пункта меню и его значение на первой строке дисплея

  for (int i = 0; i < LINES; i++) {
    lcd.setCursor(0, i);
    if (arrowPos == LINES * screenPos + i) lcd.write(126);  // рисуем стрелку
    else lcd.write(32);     // рисуем пробел

    // если пункты меню закончились, покидаем цикл for
    if (LINES * screenPos + i == SETTINGS_AMOUNT) break;
    lcd.print(settingsNames[LINES * screenPos+i]);
    lcd.setCursor(12, i);
    // if(param[LINES * screenPos+i].name == "AutoMode"){
    if(settingsNames[LINES * screenPos+i]== "ModeA"){
      lcd.print(param[LINES * screenPos+i].mode?"true":"false");
    }else{
    lcd.print(param[LINES * screenPos+i].value);
    }
  }
}

void loop(){
  enc1.tick();
  //Если было двойное нажатие то записываем данные по умолчанию
  if(enc1.isDouble()){
    writeParamStruct();
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Default");
    delay(5000);
    displayMenu();
  }


  if (enc1.isTurn()){
    //Блок выбора пункта меню
    int increment =0;                         
    if(enc1.isRight()) increment =1;
    if(enc1.isLeft()) increment =-1;
    arrowPos += increment;
    arrowPos = constrain(arrowPos,0,  SETTINGS_AMOUNT -1);

    //Блок изменения значения пункта меню при удержании нажатия и прокручивании энкодера.
    increment = 0;  // обнуляем инкремент
    if(enc1.isRightH()){
      switch(arrowPos){
        case 0: param[arrowPos].value += 5; break;
        case 1: param[arrowPos].value += 1; break;
        case 2: param[arrowPos].value += 1; break;
        case 3: param[arrowPos].value += 1; break;
        case 4: param[arrowPos].value += 1; break;
        case 5: param[arrowPos].mode =!param[arrowPos].mode; break;
      }
    }
    if(enc1.isLeftH()){
      switch(arrowPos){
        case 0: param[arrowPos].value -= 5; break;
        case 1: param[arrowPos].value -= 1; break;
        case 2: param[arrowPos].value -= 1; break;
        case 3: param[arrowPos].value -= 1; break;
        case 4: param[arrowPos].value -= 1; break;
        case 5: param[arrowPos].mode = !param[arrowPos].mode; break;
      }
    }
 
    // if (enc1.isRightH()) increment = 1;
    // if (enc1.isLeftH()) increment = -1;
    // param[arrowPos].value +=increment;

    displayMenu();
  }

  //Запись данных при удержании нажатия энкодера
  if(enc1.isHolded()){
    // EEPROM.begin(100);
    //Сохранение изменения в нескольких значениях.
    for(int i = 0; i < 6;i++){
      int address = i * sizeof(struct MenuItem); //Получаем адресс выбранной ячейки
      EEPROM.put(address,param[i]); //Записывавем по актуальные данные по выбранному меню
    }
    EEPROM.put(5,param[5].mode); //Тут уже внаглую пробую запихать данные которые были изменены.

    // EEPROM.put(0,param);
    EEPROM.commit();

    lcd.clear();

    lcd.setCursor(0,0);
    lcd.print("Write");
    delay(5000);
    displayMenu();
   }
  }