Lire et modifier une variable String dans une structure

Bonjour,

Je recherche à intégrer une variable String dans une structure ainsi:

struct MaStructure {
  byte ID;
  String HWPRIORITY;
};

MaStructure ms;

Lui donner une valeur, par exemple:

HWPRIORITY = "PNPNPN";

Puis sauvegarder en EEPROM ma structure:

EEPROM.put(0, ms);

Enfin lire ma structure dans le setup et visualiser la variable HWPRIORITY:

EEPROM.get(0,ms);
Serial.print("Var=");Serial.println(ms.HWPRIORITY);

Jusque là, ça fonctionne.

Mais si je veux modifier ma variable et la sauvegarder à l'aide de la console série, ma variable est mal sauvegardée.

J'ai donc une solution qui fonctionne parfaitement mais avec une variable String définie en dehors de ma structure.

Voici mon code actuel qui fonctionne parfaitement:

#include <EEPROM.h>

#define CARRIAGE_RETURN     0x0D /* '\r' = 0x0D (code ASCII) */
#define RUB_OUT             0x08
#define CFG_MSG_MAX_LENGTH  10//22 /* Longest Rx or Tx Message (Message le plus long est S=I 12) donc 8 devrait suffir*/
static char                 CfgMessage[CFG_MSG_MAX_LENGTH + 1];/* + 1 pour fin de chaine */


struct MaStructure {
  byte ID;
};

uint8_t Inputs_Alarms[]={18,19,20,21,15,14};
bool SoundPriorities[]={0,0,0,0,0,0};
String HWPRIORITY;

MaStructure ms;

void setup() {

  Serial.begin(115200);

  EEPROM.get(0,ms);

  for (uint8_t i = 0; i<6; i++)
  {
    pinMode(Inputs_Alarms[i], INPUT_PULLUP); // 6 Inputs pullup setup
  }


  /* Priority management of sounds */
  // declaring character array (+1 for null terminator) 
  char* char_array = new char[HWPRIORITY.length() + 1];
  // copying the contents of the 
  // string to char array 
  strcpy(char_array, HWPRIORITY.c_str());
  //Serial.print("Sounds Priorities:");
  for (uint8_t x = 0; x < HWPRIORITY.length() ;x++)
  {
    if (char_array[x] == 'P')
      SoundPriorities[x] = true;
    // Serial.print(SoundPriorities[x]);//Inputs_Alarms[i]
    // if (x < HWPRIORITY.length()) Serial.print(" ");

  }
  Serial.println();
  /* Priority management of sounds */

}

void loop() {
  if(CfgMessageAvailable() >= 0)
  {
    InterpreteCfgAndExecute();
  }

    /* Read 6 alarms inputs */
    for (uint8_t i = 0; i<6; i++)
    {
      if ((digitalRead(Inputs_Alarms[i]) == LOW) && (SoundPriorities[i] == true))
      {
        Serial.print("Alarm: ");Serial.print(i+1);Serial.println(" Started !");
      }
    }
    /* Read 6 alarms inputs */
}

void writeStringToEEPROM(int addrOffset, const String &strToWrite)
{
  byte len = strToWrite.length();
  EEPROM.write(addrOffset, len);
  for (int i = 0; i < len; i++)
  {
    EEPROM.write(addrOffset + 1 + i, strToWrite[i]);
  }
}
String readStringFromEEPROM(int addrOffset)
{
  int newStrLen = EEPROM.read(addrOffset);
  char data[newStrLen + 1];
  for (int i = 0; i < newStrLen; i++)
  {
    data[i] = EEPROM.read(addrOffset + 1 + i);
  }
  data[newStrLen] = '\0';
  return String(data);
}

void readAllEEprom()
{
  EEPROM.get(0,ms);// Read all EEPROM settings in one time
  if ( ms.ID != 0x99)
  {
    SettingsWriteDefault();
    delay(500);
    readAllEEprom();
  }
  else
  {
    //EEPROM Ok!
    HWPRIORITY = readStringFromEEPROM(100);
		Serial.print("Prior:     ");Serial.println(HWPRIORITY);	

    Serial.println();
  }
}

void SettingsWriteDefault()
{
  ms.ID        = 0x99;//write the ID to indicate valid data
  HWPRIORITY = "NNNNNN";
  writeStringToEEPROM(100,HWPRIORITY);

  Serial.println("Defaults Value Saved\r\n");
  EEPROM.put(0, ms);
}

static char CfgMessageAvailable(void)
{
  char Ret = -1;
  char RxChar;
  static uint8_t Idx = 0;

  if(Serial.available() > 0)
  {
    RxChar = Serial.read();
    switch(RxChar)
    {
      case CARRIAGE_RETURN: /* Si retour chariot: fin de message */
        CfgMessage[Idx] = 0;/* Remplace CR character par fin de chaine */
        Ret = Idx;
        Idx = 0; /* Re-positionne index pour prochain message */
        break;
      case RUB_OUT:
        if(Idx) Idx--;
        break;
      default:
        if(Idx < CFG_MSG_MAX_LENGTH)
        {
          CfgMessage[Idx] = RxChar;
          Idx++;
        }
        else Idx = 0; /* Re-positionne index pour prochain message */
        break;
    }
  }
  return(Ret); 
}

#define COMMAND       ( CfgMessage[0] )
#define ACTION        ( CfgMessage[1] )
#define ARG           ( CfgMessage[2] )

#define REQUEST       '?'
#define ORDER         '='

#define EMPTY_CMD      0
#define HELP_CMD      'H'

#define MS_SETTINGS   'S'// Read settings from EEPROM
#define MS_PRIORITIES 'I'// Define priorities

enum {ACTION_ANSWER_WITH_REPONSE = 0, ACTION_ANSWER_ERROR};

static void InterpreteCfgAndExecute(void)
{
  uint8_t Val8;
  uint8_t Action = ACTION_ANSWER_ERROR;
  char *CharPtr;

  switch(COMMAND)
  {
    case EMPTY_CMD: /* No break: Normal */
    case HELP_CMD:
      DisplayHelp();
      CfgMessage[1] = 0; /* Echo */
      Action = ACTION_ANSWER_WITH_REPONSE;
      break;
    case MS_SETTINGS://S
      if(ACTION == ORDER)
      {
        Val8 = (uint8_t)atoi(&ARG);
        if(ARG == 'W')//S=W Writedefault config
        {
          SettingsWriteDefault();
        }
        EEPROM.put(0,ms);
        CfgMessage[1] = 0;
        Action = ACTION_ANSWER_WITH_REPONSE;
      }
      else if(ACTION == REQUEST)//S?
      {
        readAllEEprom();
        CfgMessage[1] = 0;
        Action = ACTION_ANSWER_WITH_REPONSE;
      }
      break;

    case MS_PRIORITIES://I
      if(ACTION == ORDER)
      {
        if ((ARG == 'P') || (ARG == 'N'))
        {
          /* Priority management of sounds */
          String n = &ARG;
          if (n.length()!=6) {
            Serial.println("Use 6 N or P !");
            CfgMessage[1] = 0;          
            Action = ACTION_ANSWER_WITH_REPONSE;
            break;
          }
          HWPRIORITY = n;
          writeStringToEEPROM(100,HWPRIORITY);
          Serial.print("Prior:");Serial.println(HWPRIORITY);
          /* Priority management of sounds */
          CfgMessage[1] = 0;          
          Action = ACTION_ANSWER_WITH_REPONSE;
        }
        else 
        {
          Serial.println("only 6 N or P !");
          CfgMessage[1] = 0;          
          Action = ACTION_ANSWER_WITH_REPONSE;            
        }
      }
      else if(ACTION == REQUEST)
      {
        Serial.println(HWPRIORITY);
        CfgMessage[1] = 0;          
        Action = ACTION_ANSWER_WITH_REPONSE;
      }
    break;

  }
  if(Action != ACTION_ANSWER_WITH_REPONSE)
  {
    strcpy_P(CfgMessage, PSTR("ERR"));
  }
  Serial.println(CfgMessage);

}

void DisplayHelp(void)
{
  Serial.print("\r\nH Help\r\n");
  Serial.print("W Board Connections\r\n");
  Serial.print("S=W Write Default Settings\r\n");
  Serial.print("S? Read Settings\r\n");
  Serial.print("I=XXXXXX X=N or P (Priorities)\r\n");
  Serial.print("\r\n");
}

J'ai cru comprendre qu'il vaudrait mieux utiliser le type char * dans la structure.

Auriez vous une solution pour simplifier mon code ?

Merci par avance,

Pierre

EEPROM.put ne sait pas gérer des types complexes. Une String est un objet complexe car ses données ne sont pas dans l'objet, il n'y a dans l'instance que le pointeur vers la place en RAM où le texte réside. Donc si vous faites un put(), vous stockez la valeur du pointeur mais vous n'êtes pas aller chercher les données.

il faut allouer les caractères

const byte tailleMax = 10+1; // 10 caractères max
struct MaStructure {
  byte ID;
  char HWPRIORITY[tailleMax];
};

et pour allouer un texte vous ne pouvez plus utiliser l'affectation, il faut les fonction C de copie

MaStructure uneStructure;
uneStructure.ID = 42;
strncpy(uneStructure.HWPRIORITY, "PNPNPN", tailleMax); 

sauf si tout est statique

const MaStructure lesPriorites[] = {
  {0, "Priorité 0"},
  {1, "P1"},
  {2, "P2"},
  {3, "P3"},
};

Non, parce que dans ce cas ce serait comme la String, vous sauveriez en EEPROM la valeur du pointeur et pas les données pointées. il faut vraiment allouer les octets à cet endroit, dans la structure.

PS/ prenez l'habitude avec les c-string d'utiliser les fonctions avec n ou l comme strncpy() ou snprinf() ou strlcat() .... au lieu de strcpy(), sprintf(), strcat(), .... Cela évite tout risque de débordement du buffer.

1 Like

Je vous remercie :+1:.
Je regarde ça très vite.

Bonjour, votre code fonctionne parfaitement !
Merci :slightly_smiling_face:

Je tente d'utiliser cette même méthode avec une variable PULSEMODE constituée de 0 et de 1 et ça ne fonctionne pas.

Voici mon exemple:

const byte tailleMax = 10+1; // 10 caractères max
struct MaStructure {
  byte ID;
  char PULSEMODE[tailleMax];
};

MaStructure ms;

puis:

MaStructure uneStructure;
uneStructure.ID = 42;
strncpy(uneStructure.PULSEMODE, "10101010", tailleMax); 

puis je sauvegarde ma structure:

EEPROM.put(0,ms);

Je voudrais ensuite convertir ma variable ms.PULSEMODE , soit en une valeur décimale, soit binaire du type B10101010 .
Mais un Serial.print(ms.PULSEMODE,BIN) me retourne toujours un 0.

Je ne vois pas la solution :frowning:

Oups, j'ai trouvé mon erreur :slight_smile: .
J'utilisais la mauvaise variable.

:wink:

tant mieux si ça marche. Notez que si vous voulez stocker réellement des 1 et des 0, utiliser une représentation sous forme de nombre sera bien plus efficace. 4 octets (uint32_t) vous permettent de représenter 32 bits donc 32 1 ou 0... (et Si vous n'en avez que 16 prenez un uint16_t et si vous n'en avez que 8 ou moins un uint8_t)

Merci :slight_smile:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.