Get External IP Address

Hi,

I need to check me external IP address so I found a code for a ESP_32 and it's working but... This code is (serial) printing more information than I need and since I want the IP to be sent to and OLED or LCD, I need to find a way to extract only the IP to send it in a variable and then print it on my OLED or LCD screen. I also need to compare it to the previous IP Check in order to get an alarm if the IP change.

This is what the code (serial) print right now:

{"ip":"70.85.117.188"}HTTP/1.1 200 OK
Server: nginx/1.25.2
Date: Tue, 03 Oct 2023 12:39:20 GMT
Content-Type: application/json
Content-Length: 22
Connection: keep-alive
Vary: Origin

This is the code I found

#include <Arduino.h>
#if defined(ESP32)
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#else

#endif

#include <ESP_Mail_Client.h>
#define WIFI_SSID "MyRooterName"
#define WIFI_PASSWORD "MySSID_Pass"

int connectedLED = 16;

void setup(){

Serial.begin(9600);
digitalWrite(connectedLED, LOW);
pinMode(connectedLED, OUTPUT);

#if defined(ARDUINO_ARCH_SAMD)
  while (!Serial);
  Serial.println();
  Serial.println("");
#endif

wi_fi_startup();

}

void wi_fi_startup(){

  Serial.print("Connecting to AP");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED){
  Serial.println(".");
  delay(200);}
  
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("Ready");
  delay(2000);
}

void connectionCheck(){

  if(WiFi.status()>= WL_CONNECTED){ 
  digitalWrite(connectedLED, HIGH);
  }else{
  digitalWrite(connectedLED, LOW);
 }
}

void GetExternalIP()
{
  WiFiClient client;
  if (!client.connect("api.ipify.org", 80)) {
    Serial.println("Failed to connect with 'api.ipify.org' !");
  }
  else {
    int timeout = millis() + 5000;
    client.print("GET /?format=json HTTP/1.1\r\nHost: api.ipify.org\r\n\r\n");
    while (client.available() == 0) {
      if (timeout - millis() < 0) {
        Serial.println(">>> Client Timeout !");
        client.stop();
        return;
      }
    }
    int size;
    while ((size = client.available()) > 0) {
      uint8_t* msg = (uint8_t*)malloc(size);
      size = client.read(msg, size);
      Serial.write(msg, size);
      free(msg);
    }
  }
}

void loop(){

connectionCheck();
GetExternalIP();

delay(500);
}

If I type "api.ipify.org" in my browser I see my external IP so I know this is probably where I need to begin but getting help would be nice.

Thanks.

the request you send is actually http://api.ipify.org/?format=json

the answer you get starts with a JSON {"ip":"70.85.117.188"}
and then confirmation that the request was OK HTTP/1.1 200 OK

you should use the HTTPClient class and you can get access to the answer directly

try this code:

#include <WiFi.h>
#include <HTTPClient.h>

const char * ssid       = "xxx";
const char * password   = "xxx";
const char * jsonURL = "http://api.ipify.org/?format=json";
const char * textURL = "http://api.ipify.org";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());


// Obtain the IP address in a JSON String

  if (WiFi.status() == WL_CONNECTED) {
    WiFiClient client;
    HTTPClient http;
    String payload = "";

    http.begin(client, jsonURL);
    int httpResponseCode = http.GET();

    if (httpResponseCode > 0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
      payload = http.getString();
      Serial.print("the server provided this JSON : ");
      Serial.println(payload);
    }
    else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }
    http.end();
  }

// obtain the IP address just as text in a String

  if (WiFi.status() == WL_CONNECTED) {
    WiFiClient client;
    HTTPClient http;
    String payload = "";

    http.begin(client, textURL);
    int httpResponseCode = http.GET();

    if (httpResponseCode > 0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
      payload = http.getString();
      Serial.print("the server provided this text : ");
      Serial.println(payload);
    }
    else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }
    http.end();
  }

  
}

void loop() {}

open the serial monitor at 115200 bauds, you should see two answers

HTTP Response code: 200
the server provided this JSON : {"ip":"70.85.117.188"}
HTTP Response code: 200
the server provided this text : 70.85.117.188

you don't need the JSON format, I would probably go for the pure text form, in that case you can create easily an IPAddress instance by parsing the payload:

      IPAddress wanIP;
      if (wanIP.fromString(payload)) {
        Serial.print("Connected to WiFi network with WAN IP Address: ");  Serial.println(wanIP);
      } else {
        Serial.println("Invalid IP address format");
      }

and you'll see

Connected to WiFi network with WAN IP Address: 70.85.117.188

Hi J-M-L,

My ESP32 didn't like your code. After upload the code to the ESP32, this is what I saw on the serial monitor.

rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10944
load:0x40080400,len:6388
entry 0x400806b4

But the strange part is.. Even if I want to put back my old code, it doesn't take it anymore. The serial speed of my code was 9600 and when I fininsh the upload of my old code (the one in my post) I have to leave the serial monitor at 115200 to see the error message. If it's still at 115,200, it's probably because your code is still in. I wonder how I can reset it now.

Upload a simple Hello World that prints to the console and be sure to set your baud before sending it. At that point it should be set to what you want.

Why in the world would you prefer 9600 over 115200?

9600 or 115,200 is not the point.

No, I just tried and it is still at 115,200 with the error message from the get Ip code.

BUT

I just tried 3 other ESP-32 and I get the same problem. The bug is not from the code because even with a new Never programmed ESP-32, I have the same probleme after just uploading the simple code hello world. I don't know why because yesterday I was able to upload it ok.

This topic is now done until I find why I'm unable to programme an ESP-32 with IDE. When this is done I will continu to work on how to get a Wan IP address.

Since you did not follow the forum guidelines your code is not very readable. Did you put a serial.begin(nnnn); in your setup? If not add the begin statement and nnnn becomes the baud you want.

Make sure NOTHING is connected to your ESP32 besides the USB cable

if you compile and upload

unsigned long count = 0;

void setup() {
  Serial.begin(115200);
  while(!Serial) delay(1);
  Serial.println("Hello from ESP32");
}

void loop() {
  Serial.println(count++);
  delay(1000);
}

and open the Serial monitor at 115200 bauds, what do you see?

Hi J-M-L,

I did your test and this is what I see at 115,200

rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1344
load:0x40078000,len:13964
load:0x40080400,len:3600
entry 0x400805f0
ets Jul 29 2019 12:21:46

The funny thing is that if I do the same test but with a speed of 9600 in the code, I will see the same error message but at 115,200. If I put the serial monitor at 9600, it this.

⸮L1I⸮⸮ ⸮J,⸮⸮z⸮⸮ ⸮ ⸮%⸮@⸮p'@C⸮'#⸮⸮⸮,⸮V⸮⸮g;⸮-⸮\⸮ ⸮ ⸮
⸮`'#5⸮⸮b(⸮$⸮⸮⸮⸮ )E⸮⸮⸮⸮X⸮ ⸮ J,Ķ⸮⸮"⸮!C⸮

But forget all that for now. (new test)

The probleme is not from the code or the ESP32.
I did the test with four ESP32 (3 of them brand new in the bag) and it's all the same and nothings is or was connected to those ESP32.

I just took my other laptop. It's the same model of laptop with the same windows (A backup laptop with a clone of my disk) it means it's the same arduino, library etc..

And with this laptop it's all working.

I don't understand what append because yesterday morning it was all ok. I was able to test some code and see the IP Address but of course with the jason parsing and it's only after I tried your code that everything when crazy. I know it has nothing to do with your code but now I need to find a way to fix my IDE software. And the bug is only with the ESP32 because I tried with a 8266 and it was ok.

So, the IDE software went crazy yesterday morning and that's it.

I just update the ESP32 in board manager but same.

I will try to copy the IDE folder from backup laptop to my main laptop to see what's appening.

Thanks for your help

which version of the IDE are you using ?

version 1.8.15 but remember, it's working on my backup laptop with that version.

I just copied the folder arduino from program file but the bug is still there. I'm going to copy the folder in App Data / Local this time and see.

You’d probably need to do a proper install

Hi J-M-L,

Sometimes I prefer to start with the folder from program file and after AppData and then if it doesn't work I do a proper install.

I just replace C:\Users\gilles\AppData\Local\Arduino15 with the one in my backup laptop and everything is back to normal.

I will never know how the bug came in but now that it's working, I can go back to the real problem and subject of my post (Get the Wan Ip Address)

I'm starting tests with your code again and I will have more questions for you after.

Question

The device I want to built with the ESP32 is to watch my IP address and if the IP change a buzzer will tell me.

My idea is to check the IP and with a button it will store the value in the EEPROM to keep it in memory. The code will compare the actual Wan IP every X time and tell me when the new value is not equal to the one in the EEPROM.

I'm okay with buttons, buzzer, write to EEPROM and read from it, timing etc.. but since the response will be the payload as a string, I'm not sure how to deal with string to compare.

How can I compare two IP as a string ?

BTW

Many thanks to take the time to help me and I hope my english is not too bad (I'm french)

Hey what can I say, nobody's perfect :slight_smile:

If you use the second code you call .c_str() on the payload to extract the pointer to the text string (a cString) . That’s what you could store in "EEPROM’" easily. To compare two cStrings look at strcmp()

If you go to the object representation with an IPAddress then == will do

I’m French too :wink: so I get your English
the French speaking part of the forum is pretty active if you. Feel you would be better served in your native language

Hi, J-M-L

Sorry for the delay of reply but I'm not always at home. Your code is ok for the jason part but not for the text.

See the result

17:51:27.912 -> Connecting
17:51:28.382 -> .
17:51:28.382 -> Connected to WiFi network with IP Address: 192.168.3.105
17:51:28.851 -> HTTP Response code: 200
17:51:28.898 -> the server provided this JSON : {"ip":"67.69.76.78"}
17:51:29.180 -> HTTP Response code: 404
17:51:29.180 -> the server provided this text :

and then the code stopped, I mean the code doesn't loop. oups I just saw why.. all the code is in the setup part of the code

may be the server does not like two request quickly, get rid of the first one to see if you still get 404 (not found)

#include <WiFi.h>
#include <HTTPClient.h>

const char * ssid       = "xxx";
const char * password   = "xxx";
const char * textURL = "http://api.ipify.org"; // or https://api64.ipify.org to support ipv6

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());

  // obtain the IP address just as text in a String

  if (WiFi.status() == WL_CONNECTED) {
    WiFiClient client;
    HTTPClient http;
    String payload = "";

    http.begin(client, textURL);
    int httpResponseCode = http.GET();

    if (httpResponseCode > 0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
      payload = http.getString();
      Serial.print("the server provided this text : ");
      Serial.println(payload);
    }
    else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }
    http.end();
  }

  
}

void loop() {}

No I found the bug. I just add a / after .org for the textURL

const char * textURL = "http://api.ipify.org/";

It's okay now.

This is what I see now

18:27:11.912 -> HTTP Response code: 200
18:27:11.960 -> the server provided this JSON : {"ip":"67.69.76.78"}
18:27:12.241 -> HTTP Response code: 200
18:27:12.288 -> the server provided this text : 67.69.76.78

About what you told me for the compare.

*If you use the second code you call .c_str() on the payload to extract the pointer to the text string (a cString) . That’s what you could store in "EEPROM’" easily. To compare two cStrings look at strcmp()

If you go to the object representation with an IPAddress then == will do*

This is over my knowledge of programmation so I may need a little more help.

I didn't change your code but I removed the Jason part and split the code in different fonction and make it loop every five seconds.

Now I have to figure how to include what you told me about the pointer etc..


//Code from J-M-L on the Arduino Forum

#include <WiFi.h>
#include <HTTPClient.h>

const char * ssid       = "XXXXXXX";
const char * password   = "XXXXXXXXX";
const char * textURL = "http://api.ipify.org/";
String payload = "";

void setup() {
Serial.begin(9600);
WiFi.begin(ssid, password);
Serial.println("Connecting");

while (WiFi.status() != WL_CONNECTED){
  delay(500);
  Serial.print(".");
  }
Serial.println("");
Serial.print("Connected to WiFi Local IP Address: ");
Serial.println(WiFi.localIP());
}

// obtain the IP address just as text in a String
void GetText(){
  if (WiFi.status() == WL_CONNECTED) {
    WiFiClient client;
    HTTPClient http;

    http.begin(client, textURL);
    int httpResponseCode = http.GET();

    if (httpResponseCode > 0) {
      payload = http.getString();
    }
    else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }
    http.end();
  }
}

void PrintData(){

  Serial.print("WAN IP: ");
  Serial.println(payload);
}

void loop(){

GetText(); 
PrintData();
delay(5000); 
}

try this code (typed here from my iPhone so...)

#include <WiFi.h>
#include <HTTPClient.h>
#include <Preferences.h>
Preferences preferences;

const char * ssid       = "xxx";
const char * password   = "xxx";
const char * textURL = "http://api.ipify.org/";

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());


  if (WiFi.status() == WL_CONNECTED) {
    WiFiClient client;
    HTTPClient http;
    String payload = "";

    http.begin(client, textURL);
    int httpResponseCode = http.GET();

    if (httpResponseCode > 0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
      payload = http.getString();
      Serial.print("the server provided this text : ");
      Serial.println(payload);


      IPAddress wanIP;
      if (wanIP.fromString(payload)) {

        Serial.print("Connected to WiFi network with WAN IP Address: ");  Serial.println(wanIP);


        uint32_t wanIP32bits = wanIP;
        Serial.print("wanIP32bits: 0x");  Serial.println(wanIP32bits, HEX);

        preferences.begin("IPStorage", false);
        uint32_t oldIP32bits = preferences.getULong("oldIP", 0xFFFFFFFF);
        Serial.print("read old IP as 0x");  Serial.println(oldIP32bits, HEX);

        if (oldIP32bits == wanIP32bits) {
          Serial.println("IP address is the same");
        } else {
          Serial.println("IP address has changed");
          preferences.putULong("oldIP", wanIP32bits);
        }

        preferences.end();
      } else {
        Serial.println("Invalid IP address format");
      }
    }
    else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }
    http.end();
  }


}

void loop() {}

run it first time, with the Serial monitor opened at 115200 bauds.

if all goes well, It will tell you the old IP was 0xFFFFFFFF (because none was stored) and it has changed to your new one which will get stored.

press the reboot button now to launch again the code. This time the oldIP is no longer 0xFFFFFFFF, it's the one you read during the previous run and it should tell you it has not changed