Click here to Skip to main content
15,946,342 members
Articles / Internet of Things / Arduino
Tip/Trick

IP Geolocation with an ESP32 and Arduino

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
9 May 2024CPOL 4.6K   2   3
Get location information based on your IP address using an ESP32 and ip-api.com
Presenting a simple fetch class that talks to ip-api.com to get various information about your location with an ESP32 IoT widget

Introduction

I've produced this with a couple of projects but never really talked about it much, and I have since improved on the code. It deserves its own tip at least.

Without a GPS how do you get your time zone? NTP services don't provide it. The answer is IP geolocation, and http://ip-api.com fits the bill nicely.

Prerequisites

  • You'll need VS Code w/ PlatformIO installed
  • You'll need an ESP32

Using the code

The API is simple to use. You'll need to connect your WiFi before using it.

Add the following library to your project: codewitch-honey-crisis/htcw_json

Copy these files into your project:

ip_loc.hpp

C++
#pragma once
#ifndef ESP32
#error "This library only supports the ESP32 MCU."
#endif
#include <Arduino.h>
namespace arduino {
struct ip_loc final {    
    static bool fetch(float* out_lat,
                    float* out_lon, 
                    long* out_utc_offset, 
                    char* out_region, 
                    size_t region_size, 
                    char* out_city, 
                    size_t city_size,
                    char* out_time_zone,
                    size_t time_zone_size);
};
}

ip_loc.cpp

C++
#ifdef ESP32
#include <ip_loc.hpp>
#include <HTTPClient.h>
#include <json.hpp>
namespace arduino {
static char* ip_loc_fetch_replace_char(char* str, char find, char replace){
    char *current_pos = strchr(str,find);
    while (current_pos) {
        *current_pos = replace;
        current_pos = strchr(current_pos+1,find);
    }
    return str;
}
bool ip_loc::fetch(float* out_lat,
                float* out_lon, 
                long* out_utc_offset, 
                char* out_region, 
                size_t region_size, 
                char* out_city, 
                size_t city_size,
                char* out_time_zone,
                size_t time_zone_size) {
    // URL for IP resolution service
    char url[256];
    *url = 0;
    strcpy(url,"http://ip-api.com/json/?fields=status");//,region,city,lat,lon,timezone,offset";
    int count = 0;
    if(out_lat!=nullptr) {
        *out_lat = 0.0f;
        strcat(url,",lat");
        ++count;
    }
    if(out_lon!=nullptr) {
        *out_lon = 0.0f;
        strcat(url,",lon");
        ++count;
    }
    if(out_utc_offset!=nullptr) {
        *out_utc_offset = 0;
        strcat(url,",offset");
        ++count;
    }
    if(out_region!=nullptr && region_size>0) {
        *out_region = 0;
        strcat(url,",region");
        ++count;
    }
    if(out_city!=nullptr && city_size>0) {
        *out_city = 0;
        strcat(url,",city");
        ++count;
    }
    if(out_time_zone!=nullptr && time_zone_size>0) {
        *out_time_zone = 0;
        strcat(url,",timezone");
        ++count;
    }

    HTTPClient client;
    client.begin(url);
    if(0>=client.GET()) {
        return false;
    }
    Stream& stm = client.getStream();
    io::arduino_stream astm(&stm);
    json::json_reader_ex<512> reader(astm);
    while(reader.read()) {
        if(reader.depth()==1 && reader.node_type()==json::json_node_type::field) {
            if(out_lat!=nullptr && 0==strcmp("lat",reader.value())) {
                reader.read();
                *out_lat = reader.value_real();
                --count;
            } else if(out_lon!=nullptr && 0==strcmp("lon",reader.value())) {
                reader.read();
                *out_lon = reader.value_real();
                --count;
            } else if(out_utc_offset!=nullptr && 0==strcmp("offset",reader.value())) {
                reader.read();
                *out_utc_offset = reader.value_int();
                --count;
            } else if(out_region!=nullptr && region_size>0 && 0==strcmp("region",reader.value())) {
                reader.read();
                strncpy(out_region,reader.value(),region_size);
                --count;
            } else if(out_city!=nullptr && city_size > 0 && 0==strcmp("city",reader.value())) {
                reader.read();
                strncpy(out_city,reader.value(),city_size);
                --count;
            } else if(out_time_zone!=nullptr && time_zone_size>0 && 0==strcmp("timezone",reader.value())) {
                reader.read();
                strncpy(out_time_zone, reader.value(),time_zone_size);
                ip_loc_fetch_replace_char(out_time_zone,'_',' ');
                --count;
            }
        } else if(count<1 || reader.depth()==0) {
            // don't wait for end of document to terminate the connection
            break;
        }
    }
    client.end();
    return true;
}
}
#endif

To use it you call ip_loc::fetch() passing nulls and zeroes for any arguments you don't need, but otherwise providing buffers for the out data. The string data requires you to tell it how big the buffer is.

C++
#include <ip_loc.hpp>
...
long time_offset;
char time_zone_buffer[128];
...
// grabs the timezone and tz offset based on IP
arduino::ip_loc::fetch(
    nullptr,
    nullptr,
    &time_offset,
    nullptr,
    0,
    nullptr,
    0,
    time_zone_buffer,
    sizeof(time_zone_buffer)
);

History

  • 9th May, 2024 - Initial submission

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Just a shiny lil monster. Casts spells in C++. Mostly harmless.

Comments and Discussions

 
QuestionInteresting but not accurate Pin
gunamoi120-May-24 22:09
gunamoi120-May-24 22:09 
AnswerRe: Interesting but not accurate Pin
honey the codewitch21-May-24 1:04
mvahoney the codewitch21-May-24 1:04 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA14-May-24 3:44
professionalȘtefan-Mihai MOGA14-May-24 3:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.