Extracting IP address from a character array returned by ESP8266 module

Hi all
I am building a project with an Arduino Nano connected to an ESP8266-01 wifi module
I have it all communicating, connecting to wifi etc by sending AT commands to it and receiving from it via AltSoftSerial.

My issue is this:-
I have a character array for the incoming data from the module, declared as

char incoming[100];

When I issue the AT+CIFSR command I get a the following :-

+CIFSR:STAIP,"192.168.1.117"
+CIFSR:STAMAC,"xx:xx:xx:xx:xx:xx"

I am trying to create a function which will extract that IP address (ie the characters between the double quotes) and return that in its own char array. ie in this case the characters at position 14 to 26 inclusive

I have successfully used strstr(incoming,"STAIP") to give me an index to start searching from but I have no idea how to progress from there

Could anybody help please?

I cant post the code at the moment as I am at work and the laptop is at home. I have managed to avoid the use of String objects in the code to prevent memory issues but am struggling on how to perform this operation

You can use strstr to find both quotation marks, then use strncpy to copy the correct part of the IP address into the new char array:

  char inString[] = "+CIFSR:STAIP,\"192.168.1.117\"";
  char IP[16];
  char* firstQuote;
  char* secondQuote;
  Serial.begin(115200);
  delay(100);
  Serial.println();
  Serial.println(inString);

  firstQuote = strstr(inString,"\"");
  secondQuote = strstr(firstQuote+1,"\"");
  char lengthOfIP = secondQuote - firstQuote - 1;
  strncpy(IP,firstQuote+1,lengthOfIP);
  IP[lengthOfIP] = '\0'; 

  Serial.println(IP);

but this ideally needs some checks to make sure that both quote marks actually exist and that they are a sensible space apart.
[edit]
Just realised there is a 'quicker' function, strtok() http://www.cplusplus.com/reference/cstring/strtok/
which you can use, but my example may help you understand how to do this type of thing more generally.

Thankyou, that works perfectly to find the IP.
I wish I could see how it works but I really struggle with C. I spent many years writing BASIC so tend to find myself thinking in BASIC and trying to translate into something the Arduino will understand.
As soon as pointers enter the equation I start to get lost

The incoming string contains both lines in my original post, along with &

it display like this
+CIFSR:STAIP,"192.168.1.117"
+CIFSR:STAMAC,"5c:cf:7f:65:fa:00"
OK

but is actually the IP line, MAC line OK all contained in the character array

Your code works perfectly to find the IP address as that is the first line (and is what I was after)

Would it be possible to make it more multipurpose so if I fed it the search text (eg STAIP or STAMAC) it would return what was between the quotes after that entry please?

eg I can call the function with BetweenQuotes("STAIP") and it would return the IP or BetweenQuotes("STAMAC") and it would return the MAC address

Would it be possible to make it more multipurpose so if I fed it the search text (eg STAIP or STAMAC) it would return what was between the quotes after that entry please?

Yes, it would. Have at it. You just might learn something.

I have managed to modify it as I needed.

The new function reads as below...

void scantext(char* tag, char* value)
{
  char* label;
  char* firstquote;
  char* secondquote;
  char lengthofvalue;
  label=strstr(incoming,tag);
  if (label)
  {
    firstquote=strstr(label+1,"\"");   
    secondquote=strstr(firstquote+1,"\"");
    lengthofvalue=secondquote-firstquote-1;
    strncpy(value,firstquote+1,lengthofvalue);
    value[lengthofvalue]='\0';
   }
  else
  {
    strcpy(value,"NOT FOUND");
  }
}

The calling code for it defines the tag and value char arrays, sets tag to what I am looking for and receives the value, code snippet as below

char tag[10];
char value[25];

strcpy(tag,"STAIP");
Serial.print("Tag "); Serial.print(tag);
scantext(tag,value);
Serial.print(" Value = "); Serial.println(value);

setting tag to STAIP returns this
Tag STAIP Value = 192.168.1.117

setting tag to STAMAC returns
Tag STAMAC Value = 5c:cf:7f:65:fa:00

and setting it to TEST (which is not present) returns
Tag TEST Value = NOT FOUND

Thank you both for your assistance. I can get on with writing the rest of the code now. It's a PV energy monitor which monitors PV output, mains import/export and heater diverted power with current transformers and the Open Energy Monitor library then uploads that data every minute to thingspeak via wifi. It also displays the values on a 4 line LCD display.

Why isn't the string to parse input, too? I hate code that overuses global variables.

Now, do you know how to parse "192.168.1.117" or "5c:cf:7f:65:fa:00"?

strtok() would be handy.

Im learning as i go along.
If someone could tell me how to use all locals, passing in tag and returning value i would be over the moon.

I accept its not ideal at the moment, thats why i am on here asking for help.

If someone could tell me how to use all locals, passing in tag and returning value i would be over the moon.

"All locals" might be unrealistic. But, post all of your code, and we can help you reduce the number of global variables being used.

Please be aware that so far the code just sets everything up and connects to the wifi network, gaining an IP address and displaying that.
The actual program loop is empty (I have the energy monitor working for local display in a different sketch but have been trying to get it wireless web enabled so working on that part before importing that code as well.

#include <AltSoftSerial.h>
#include "EmonLib.h" 
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

//config data - all constants 
const byte halfwaves=20;
const int timeout=2000;
const int baudrate=9600;
const byte voltspin=0, pvpin=1, mainspin=2, heaterpin=3;

//calibration data for each input. Accounts for variations in CT outputs and AC adaptor output
const float voltcal=228.00;
const float ipvcal=59.50;
const float imainscal=59.50;
const float iheatercal=54.50;
const float phaseshift=1.50;

//wifi network data
const char ssid[] = "mySSIDhere";
const char pwd[] = "mypassword";

//ip address and GET prefix for thingspeak
const char ip[] = "184.106.153.149";                    
const char GET[] = "GET /update?key=mythingspeakkey";

// global variables for voltage, current and power for each input plus calculated use
float vpv, ipv, qpv;
float vmains, imains, qmains;
float vheater, iheater, qheater;
float housetotal;

//wifi in/out strings
char incoming[100]; 
char clientip[16];

//Objects
EnergyMonitor emonpv;           // Create an instance of energy monitor for PV
EnergyMonitor emonmains;      // Create an instance of energy monitor for mains import/export
EnergyMonitor emonheater;     // Create an instance of energy monitor for immersion heater diversion

AltSoftSerial wifiserial;             // Start up a software serial port on D8 and D9 for ESP module comms

LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address and data

void setup() {
  
  Serial.begin(baudrate );
  wifiserial.begin(baudrate );
  lcd.begin(20,4);                    // initialize the lcd for 20 chars 4 lines, turn on backlight

  wifisend("ATE0",100);           // Check comms with ESP module and turn off echo to save array space
  wifireceive();
  if (strstr(incoming,"OK"))
  {
    Serial.println(F("Response received from ESP8266"));
    lcdtext(0,0,"Found ESP8266");
    connecttowifi();                  // Connect module to wifi network
  } 
  else {
    Serial.println(F("No response received from ESP8266")); 
    lcdtext(0,0,"Can't find ESP8266");
  } 
}

void loop() 
{
}

bool connecttowifi()
{
  char value[25];
  char tag[10];
  char cmd[50] ="AT+CWJAP=\"";
  Serial.println(F("Connecting to wifi"));
  lcdtext(0,0," Connecting to wifi");  
  strcat (cmd,ssid);
  strcat (cmd,"\",\"");
  strcat (cmd,pwd);
  strcat (cmd,"\"");
  wifisend(cmd,0);                                           //send command to connect to local wifi network
  
  for(int counter = 0; counter< 20; counter++) //loop to allow connection time of module 
  {                                                                 //and basic progress meter on lcd
    lcdtext(counter,1,".");
    delay(500);
  }
  wifireceive(); 
  if (strstr(incoming,"OK")) {
    Serial.println(F("Connected to Access Point"));
    lcdtext(0,1," Wifi connected OK  "); 
    wifisend("AT+CIFSR",500);                          //send command to display station IP and MAC address
    wifireceive();
    Serial.println(incoming);
    strcpy(tag,"STAIP");
    scantext(tag,value);                                    //extract IP address from received data
    Serial.print(F("IP address = ")); Serial.println(value);
    lcdtext(0,3,"IP = ");
    lcdtext(5,3,value);
    return true;
  } 
  else {
    Serial.println(F("Could not connect to Access Point")); 
    lcdtext(0,1,"Wifi connect failed ");
    return false; 
  }
}

void lcdtext(int column, int row, const char text[] )
{
  lcd.setCursor(column,row);
  lcd.print(text);
}

void wifisend (const char sendthis[], int waittime)
{
  Serial.print(F("Sending: "));
  Serial.println(sendthis);
  wifiserial.println(sendthis);
  delay(waittime);
}

void wifireceive()
{
  byte index=0;
  char iochar=-1;
  while (wifiserial.available()) {
    iochar=wifiserial.read();
    incoming[index]=iochar;
    index++;
    incoming[index]='\0';      
  }   
}

void scantext(char* tag, char* value)
{
  char* label;
  char* firstquote;
  char* secondquote;
  char lengthofvalue;
  label=strstr(incoming,tag);
  if (label)
  {
    firstquote=strstr(label+1,"\"");   
    secondquote=strstr(firstquote+1,"\"");
    lengthofvalue=secondquote-firstquote-1;
    strncpy(value,firstquote+1,lengthofvalue);
    value[lengthofvalue]='\0';
   }
  else
  {
    strcpy(value,"NOT FOUND");
  }
}

The result of this successfully completing are this displayed in the serial monitor
Sending: ATE0
Response received from ESP8266
Connecting to wifi
Sending: AT+CWJAP="mySSIDhere","mypassword"
Connected to Access Point
Sending: AT+CIFSR
+CIFSR:STAIP,"192.168.1.117"
+CIFSR:STAMAC,"5c:cf:7f:65:fa:00"
OK
IP address = 192.168.1.117

and this on the LCD screen

Connecting to wifi
Wifi connected OK
IP = 192.168.1.117

You seem to have moved the 'tag' and 'value' globals into a function, which is a good start. But you could go a bit further: you have

char value[25];
char tag[10];
.......
strcpy(tag,"STAIP");
scantext(tag,value);

but the strcpy() bit is really a part of scantext() so could be done as

char value[25];
scantext("STAIP",value);

[the ..... marks here are not 'c', of course, they're just marking bits of code which aren't relevant for this discussion and don't change).

You could get rid of 'incoming[]' as a global by bringing it into connecttowifi() and extending scantext():

bool connecttowifi()
{
  char incoming[100]; 
  char value[25];
.....
.....
   scantext(incoming, "STAIP",value);

You could also save some repeated code : you have two copies of

 wifireceive();
  if (strstr(incoming,"OK")) {

so why not include the test in wifireceive():

bool wifireceive(char* inStr) {
.......
return strstr(inStr,"OK");
}

and then:

if (wifireceive(incoming)) {
....
}else
{
....
}

I haven't tested all these bits so I may have some syntax errors to catch you out :slight_smile:

Many thanks for the input, you have all been very helpful and gentle on me

I have now it working, updating the LCD display and updating thingspeak every thirty seconds

It compiles OK and runs fine on a nano although is getting short of RAM, the compiler advises....

Sketch uses 13,656 bytes (44%) of program storage space. Maximum is 30,720 bytes.
Global variables use 1,587 bytes (77%) of dynamic memory, leaving 461 bytes for local variables. Maximum is 2,048 bytes.

I also get one warning for the whole sketch, as below..
warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]

scantext("IP",clientip);

I have posted the code below. Does anyone have any input which would make it better and/or free up some memory please?

I have had to split across posts as otherwise I get a warning that my post exceeds 9000 characters.

Deeclarations, setup and loop here...

#include <AltSoftSerial.h>                            // ESP8266 comms
#include "EmonLib.h"                                  // energy monitoring functions
#include <Wire.h>                                     // for I2C functionality
#include <LiquidCrystal_I2C.h>                        // LCD display functions (over I2C)

//config data - all constants 
const byte halfwaves= 20;                             // number of zero-crossings to monitor
const int  timeout  = 2000;                           // timeout (in ms) if nothing received from CTs
const int  baudrate = 9600;                           // serial monitor and ESP8266 comms
const int  tsupdate = 30000;                          // interval to update thingspeak (in ms)
const byte voltspin = 0, pvpin = 1;                   // analogue pins for volt & current sensors
const byte mainspin = 2, heaterpin = 3;

//calibration data for each input. Accounts for variations in CT outputs and AC adaptor output
const float voltcal   = 228.00;
const float ipvcal    = 59.00;
const float imainscal = 59.00;
const float iheatercal= 55.50;
const float phaseshift= 1.50;

const float pvoffset = 0;                             // correction for pv generation (Watts)
const float mainsoffset = -100;                       // correction for import/export (Watts)
const float heateroffset = -100;                      // correction for heater reading (Watts)

const char ssid[] = "PLUSNET-CTPM";                   // wifi network ssid
const char pwd[]  = "babyb00ba";                      // wifi password

const char ip[]  = "184.106.153.149";                 // thingspeak IP address  
const char GET[] = "GET /update?key=SIL2SDM25R4JO419";// thingspeak private key

// global variables for voltage, current and power for each input plus calculated house usage
float qpv=0;
float qmains = 0;
float qheater = 0;
float housetotal = 0;

//wifi in/out strings
char incoming [100]; 
char clientip [16];

//thingspeak update variables
unsigned long oldmillis;
bool setupcomplete=false;
bool TSupdated=false;

//Objects
EnergyMonitor emonpv;                                     // Create an instance of energy monitor for PV
EnergyMonitor emonmains;                                  // Create an instance of energy monitor for mains import/export
EnergyMonitor emonheater;                                 // Create an instance of energy monitor for immersion heater diversion

AltSoftSerial wifiserial;                                 // Start up a software serial port on D8 and D9 for ESP module comms

LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address and pins on the I2C backpack

void setup() {
  
  Serial.begin(baudrate);
  wifiserial.begin(baudrate);
  lcd.begin(20,4);                                         // initialize the lcd for 20 chars 4 lines, turn on backlight
  lcdtext(2,0,"   Please wait");
  
  ESP8266command("ATE0");                                  // confirm communication with ESP8266 module
  if (ESP8266response("OK",2000))
  {
    lcdtext(1,1,"Connecting to WiFi");
    connecttowifi();                                       // now try to connect to wifi network;
    
    if(ESP8266response("OK",10000))
    {
      lcdtext(0,1,"   WiFi connected  ");
      lcdtext(0,2,"Obtaining IP address");
      ESP8266command("AT+CIFSR");                          // now request IP address and MAC address
      
      if(ESP8266response("OK",5000))
      {
        scantext("IP",clientip);
        lcdtext(0,2,"                    ");
        lcdtext(0,2,"IP = ");
        lcdtext(5,2,clientip);
    
        ESP8266command("AT+PING=\"8.8.8.8\"");              //now confirm internet connectivity by pinging 
        if(ESP8266response("OK",10000))                     //Google DNS servers
        {
          lcdtext(0,3," Internet access OK ");              //now have internet connection
          setupcomplete=true;
        }
        else
        {
          lcdtext(0,3," fail - no internet ");              //no internet connection
        }
      }
      
      else
      {
        lcdtext(0,2,"                    ");
        lcdtext(0,2," No IP - check DHCP ");                //no IP  (router issue?)
      }
    }
    else
    {
      lcdtext(0,1," Connection failed  ");                  //failed to connect to wifi
    }
  }
  else
  {
    lcdtext(0,0,"Internal comms fault");                    //no communication with ESP8266
  }

  delay(2000);                                              //keep setup/error messages on screen for 2seconds
  
  lcd.clear();                                              //opersting screen for LCD
  lcdtext(0,0,"Solar :      KW");
  lcdtext(0,1,"Import:      KW"); 
  lcdtext(0,2,"House :      KW");
  lcdtext(0,3,"Heater:      KW"); 

  //calibration of the energy monitor instances
  Serial.println(F("Calibrating Emon instances"));
  emonpv.voltage(voltspin, voltcal, phaseshift);            // Voltage: input pin, calibration, phase_shift
  emonpv.current(pvpin, ipvcal);                            // Current: input pin, calibration.
  emonmains.voltage(voltspin, voltcal, phaseshift);         // Voltage: input pin, calibration, phase_shift
  emonmains.current(mainspin, imainscal);                   // Current: input pin, calibration.
  emonheater.voltage(voltspin, voltcal, phaseshift);        // Voltage: input pin, calibration, phase_shift
  emonheater.current(heaterpin, iheatercal);                // Current: input pin, calibration.
}

void loop() {
  getenergyvalues();

  updateLCD(qpv, qmains, qheater, housetotal);
  
  // update thingspeak after first run then after every 30 seconds
  if (!TSupdated) {
    sendtothingspeak(qpv, qmains, qheater, housetotal);
    oldmillis=millis();
    TSupdated=true;
  }
  if ((millis()-oldmillis) >30000){
    sendtothingspeak(qpv, qmains, qheater, housetotal);
    oldmillis=millis();
  }
}

Rest of the code here

void updateLCD(float pv, float mains, float heater, float total)
{
  char pvpower[10];
  char mainspower[10];
  char heaterpower[10];
  char housepower[10];

  dtostrf (pv,    4,1,pvpower);
  dtostrf (mains, 4,1,mainspower);
  dtostrf (heater,4,1,heaterpower);
  dtostrf (total, 4,1,housepower);
  
  lcdtext(8, 0, pvpower);
  lcdtext(8, 1, mainspower);
  lcdtext(8, 2, housepower);
  lcdtext(8, 3, heaterpower);
}


void sendtothingspeak(float pv, float mains, float heater, float total)
{
  char datastring[100] ;                                        //holds text to send to thingspeak
  char openstring[20];                                          //holds send coomand including length of data string
  char lenstring[4];
  char pvpower[10];
  char mainspower[10];
  char heaterpower[10];
  char housepower[10];
  
  int datalength=0;
  int pvlen=3, mainslen=3, heaterlen=3, totallen=3;

  // used for the dtostrf function to ensure no extra spaces are inserted as these break thingspeak
  // will never need more than 4 characters as maximum supply from mains is 20.0KW
  if (pv>9.49) pvlen=4;
  if (mains>9.49) mainslen=4;
  if (heater>9.49) heaterlen=4;
  if (total>9.49) totallen=4;

  // convert the floats from Energy monitor to char arrays with one decimal (eg 2.1)
  dtostrf (pv,pvlen,1,pvpower);
  dtostrf (mains,  mainslen,  1, mainspower);
  dtostrf (heater, heaterlen, 1, heaterpower);
  dtostrf (total,  totallen,  1, housepower);

  // start TCP connection
  strcpy(datastring,"AT+CIPSTART=\"TCP\",\"");
  strcat(datastring,ip);
  strcat(datastring,"\",80");
  ESP8266command(datastring);
  
  if(ESP8266response("OK",5000))
  {
    //we have an active collection to thingspeak
    Serial.println(F("Connected to thingspeak"));
    lcdtext(18,0,"*");

    //build and send the GET request with the data in it
    strcpy(datastring,GET);
    strcat(datastring,"&field1="); strcat(datastring,pvpower);
    strcat(datastring,"&field2="); strcat(datastring,mainspower);
    strcat(datastring,"&field3="); strcat(datastring,heaterpower);
    strcat(datastring,"\r\n");

    //build the request to send AT+CIPSEND=xx - where xx is number of bytes in datastring
    datalength=strlen(datastring);
    strcpy(openstring,"AT+CIPSEND=");
    dtostrf(datalength,2,0,lenstring);
    strcat(openstring,lenstring);
    ESP8266command(openstring);
    
    if(ESP8266response(">",10000))
    {
      //arrow received
      ESP8266command(datastring);
      delay(200);
      if(ESP8266response("CLOSED",10000))
      {
        //received by thingspeak
        lcdtext(18,0," ");
      }
      else
      {
        //not received 
      }     
    }
    else
    {
      //not received
    }  
  }
  else
  {
    //couldnt connect to thingspeak
    Serial.println(F("Failed to connect to thingspeak"));
  }
}


bool ESP8266response(const char response[], int timeout)
{
  byte index=0;
  char iochar=-1;
  unsigned long oldmillis=millis(); 

  //clear incoming string
  incoming[0]=(char)0;
  while((millis()-oldmillis)<timeout && !strstr(incoming,response))
  {
    if(wifiserial.available()) 
    {
      iochar=wifiserial.read();
      incoming[index]=iochar;
      index++;
      incoming[index]='\0';
    }
  }
  if ((millis()-oldmillis)>=timeout) {
    return false;
  }
  else {
    Serial.println(incoming);
    Serial.print(response);
    Serial.print(F(" received after "));Serial.print(millis()-oldmillis);Serial.println(F(" milliseconds"));
    return true;
  }
}


void connecttowifi()
{
  char cmd[50] ="AT+CWJAP=\"";
  Serial.println(F("Connecting to wifi")); 
  strcat (cmd,ssid);
  strcat (cmd,"\",\"");
  strcat (cmd,pwd);
  strcat (cmd,"\"");
  ESP8266command(cmd);  
}


void ESP8266command (const char sendthis[])
{
  Serial.print(F("Sending: "));
  Serial.println(sendthis);
  wifiserial.println(sendthis);
}

void scantext(char* tag, char* clientip)
{
  char* label;
  char* firstquote;
  char* secondquote;
  char lengthofvalue;
  label=strstr(incoming,tag);
  if (label)
  {
    firstquote=strstr(label+1,"\"");   
    secondquote=strstr(firstquote+1,"\"");
    lengthofvalue=secondquote-firstquote-1;
    strncpy(clientip,firstquote+1,lengthofvalue);
    clientip[lengthofvalue]='\0';
   }
  else
  {
    strcpy(clientip,"NOT FOUND");
  }
}


void lcdtext(int column, int row, const char text[] )
{
  lcd.setCursor(column,row);
  lcd.print(text);
}


void getenergyvalues() 
{
  //first the PV values
  Serial.println(F("Getting PV power"));
  emonpv.calcVI(halfwaves,timeout);      // Calculate all. No.of half wavelengths (crossings), time-out                
  qpv   = ((emonpv.realPower + pvoffset) / 1000);           

  //now the mains values
  Serial.println(F("Getting mains power"));
  emonmains.calcVI(halfwaves,timeout);   // Calculate all. No.of half wavelengths (crossings), time-out            
  qmains  = ((emonmains.realPower+mainsoffset) / 1000);       

  //now the heater values
  Serial.println(F("Getting heater power"));
  emonheater.calcVI(halfwaves,timeout);   // Calculate all. No.of half wavelengths (crossings), time-out           
  qheater  = ((emonheater.realPower  + heateroffset) / 1000);

  housetotal = (qpv + qmains) - qheater;
}