Search code examples
arduinoconfigethernetsd-card

arduino, setup ethernet & network using data from SD config file


Im try to add to my sketch a dynamic way to setup the ethernet info (mac, ip, gateway, subnet) from a configuration file (config.txt). So running a webserver and serving htm files from sd card, user can go to setting page, fill a form with these info and when posted , the webserver parse the http form and save (update) the config.txt file. After that system do a restart, in order to start with the new settings (by read the config.txt file)

I have create succesfully all the parts (sd, ethernet, webserver, webclient, create the config file from posted form data) except the get params by reading the config.txt file.

I can read line by line the config, I can split the line to param & value, and now I need to fill some byte variables with the readed data. I can (after a month of google searching) to read IPs (decimal values) to byte array. Im stack to read the MAC ADDRESS hex into byte array. The config file contains the:

mac=8f:2c:2b:19:e0:b7;
ip=192.168.1.200;
netmask=255.255.255.0;
gateway=192.168.1.254;
dns=8.8.8.8;
posturl=192.168.1.157;
postport=8080;
postscript=/itherm/update.php;
interval=60000;

and the code that I use to read is:

byte myMAC[6];
byte myIP[4];

  File fset;
  fset = SD.open("config.txt");
  if (fset){
    char ff[40];
    while (fset.available()>1){
      bool eol=false;
      for (int i=0; !eol;i++){
        ff[i]=fset.read();
        if (ff[i]=='\n'){
           eol=true;
        }
      }
      String par="";
      bool DONE=false;
      for (int i=0; !DONE;i++){
        par+=ff[i];
        if (ff[i]== '='){DONE=true;}
      }
      String pval="";
      DONE=false;
//------------------------

      if (par=="ip=" ){
        int x=0;
        while(!DONE){
          for(int i=3;i<=i+21;i++){
            if(ff[i]=='.'){
              myIP[x]=pval.toInt();
              x++;
              i++;
              pval="";
            }
            else if(ff[i]==';' || i>20){
              myIP[x]=pval.toInt();
              DONE=true;
              break;
            }
            pval+=ff[i];
          }
        }
      }

    } //while (fset.available()>1)
  } //if (fset)

I will appreciate any help. Please no answers with simple use of Serial.print(). I have found hundreds of suggestions but none, that work properly to read all the parameters (dec, hex, strings). After a month of effort & searching, I wonder why something so necessary and useful does not exist as an example in the community, completely functional !!

Best regards


Solution

  • Okay so here is a complete set of routines to do what you want -I think you misunderstood the concept of char arrays vs a single char[0] The routines are documented and self explanatory. I recomend not to finish lines with ; but with '\n' which in your example is there anyway (also you can not see the new line terminator) To get the mac address I need three lines:

      if (strncmp(cfgLine, "mac=", 4) == 0) {
        strcpy (macAddr, cfgLine + 4);
      }
    

    line one compares the first 4 characters and if it is 0 (meaning its a fit) line two copies the chars from the fifth to the last char from the lineBuffer to the target array, which can actually be used as param for functions.
    The file structure should be with no ; as you would have to parse ; and \n

    mac=8f:2c:2b:19:e0:b7
    ip=192.168.1.200
    ....
    postport=8080
    

    To convert a char array to eg int we use atoi(), to convert a single char[0] to a single number we use int singleDigit = char[0]-48;

    const char configurationFilePath [] = "/someconfig.txt";
    char cfgLine[128] = {'\0'};  // this is a global temp char array to hold the read lines (lenght= chars longest line +1)
    char numBuffer[16] = {'\0'};  // this is a global temo char array to help to convert char to number
    
    char macAddr [18] =  {'\0'};  // this is a global char array to hold the mac address
    char ipAddr [16] =  {'\0'};  // this is a global char array to hold the IP address - max xxx.xxx.xxx.xxx
    int postport=0;
    // .... you can easyly implement for all other data you want to store/retrieve
    
    // Counts the lines of a file 
    uint16_t countLines() {
      uint16_t currentLineCount = 0;
      File cfgFile = SD.open(configurationFilePath, "r");
      if (!cfgFile) {
        Serial.println(F("Config file open failed on read"));
      } else {
        while (cfgFile.available()) {
          /** Lets read line by line from the file */
          if (cfgFile.read() == '\n') currentLineCount ++; // Lines are delimited by '\n'
        }
        cfgFile.close();
      }
      return currentLineCount;
    }
    
    //Load the config file from SD/SPIFFS/LittleFS
    bool loadConfigFile() {
      uint16_t lineCounter = countLines();
      if (lineCounter <= 0)  {
        Serial.print(F("No config data stored in file ")); Serial.println(configurationFilePath);
        return false;
      }
      else {
        File cfgFile = SD.open(configurationFilePath, "r");
        while (cfgFile.available()) {
          strcpy (cfgLine, (cfgFile.readStringUntil('\n').c_str()));  // normaly you use new line, we copy one line at a time
    //      Serial.println(cfgLine); /** Printing for debuging purpose */
          while (cfgLine[0] != '\0') { /* Block refilling of cfgLine till processed */
            loadSingleCfgLine();
          }
        }
        cfgFile.close();
        Serial.println(F("[Success] Loaded config !"));
        return true;
      }
    }
    //Load the data of a single line into a char array
    void loadSingleCfgLine() {
      if (strncmp(cfgLine, "mac=", 4) == 0) {
        strcpy (macAddr, cfgLine + 4);
      }
      if (strncmp(cfgLine, "ip=", 3) == 0) {
        strcpy (ipAddr, cfgLine + 3);
      }
      if (strncmp(cfgLine, "postport=", 9) == 0) {
        strcpy (numBuffer, cfgLine + 9);
        postport = atoi(numBuffer); // One extra step to convert to int
      }
    
    // ... easy to implement for all other data
    }
    

    I divided the routines into small independend functions, so its easy adaptable for different uses. I'm sorry for not digging into your code as it is hard to follow and unclear what you want todo.
    As an added bonus we do not use the String class. These Strings tend to fragment heap - causing resets/crashes while the global char arrays are compiled to flash and don't show this behavior.