A simple JSON capable REST HTTP Client for ESP32 IDF

readme

# HTTP REST Client

A Client library for ESP32 and [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/).

**This library is far from complete! It's missing some core features for REST (like authentication...) and its probably full of lurking C rookie mistakes, security holes, and more bugs than the amazon rainforest. I only created it to abstract away some of the boilerplate of working with REST endpoints based on the ESP-IDF docs**

_feel free to open a Pull Request to add or fix things_

# Why?

ESP-IDF provides a robust [HTTP Client library](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/esp_http_client.html)
already, why would I need this library too?

The answer is you don't need it. You probably don't even want it. But because the provided client library is robust, it also means its a bit complicated and for most people, we only need some simple http operations for interacting with REST endpoints. You shouldn't need to know how to manage chunked vs non-chunked responses or how to construct the required connection config to post a json object. I just want the ENTIRE response body loaded in a buffer I provide to do with what I want. And i don't want to copy around 40 lines of code for every request to do it!

I made this for me. If you find use in it, GREAT! If you want to improve it for you, EVEN BETTER!

# Requirements

This was developed and tested on ESP-IDF 5.0.2 and 5.1 (no guarantee it will work in earlier or future versions)

You will also will likely need to update your Max HTTP Request Header Length in the menuconfig or `sdkconfig` to something a bit higher than the default `512`.

_Note: Most modern browsers use something between `4096` and `8192` for reference. I find that `2048` is about right for most standard REST services out in the wild, but tune this for your requirements._

# Installation

Using the IDF Component Registry (Recommended)

```
idf.py add-dependency "parabuzzle/http_rest_client^0.1.0"
```

Clone a local copy to your project

```
mkdir components
cd components
git clone https://github.com/parabuzzle/idf_http_rest_client.git

## OR Use Git Submodules
git submodule add https://github.com/parabuzzle/idf_http_rest_client.git
```

# Configuration

All configuration is provided through `menuconfig` and the Kconfig.projbuild file.

# Usage

You can find a runnable working example in the [examples](/examples) directory

This is a basic idea of how it works:

_yes, its literally a single line to load the buffer with the response!_

```c
#include "http_rest_client.h"

void app_main(void)
{

  while (1)
  {
    http_rest_recv_buffer_t response_buffer = {0};

    // do the request
    ESP_ERROR_CHECK(http_rest_client_get("https://catoftheday.com/", &response_buffer));

    // print response status
    sprintf("Status Code %d", response_buffer.status_code);

    // do something with the data
    printf(response_buffer.buffer);

    // clean up
    http_rest_client_cleanup(&response_buffer);

    // wait and loop
    vTaskDelay(1000 / portTICK_PERIOD_MS);
  }

}
```

## HTTPS?

Yep!

If you have the mbed-tls certificate bundle enabled for the chip in sdkconfig, I just load it in automatically. You don't need to do anything to do https requests aside from make sure `CONFIG_MBEDTLS_CERTIFICATE_BUNDLE` is enabled.

What if you want to provide your own cert bundle pem or you can't use the mbed-tls bundle? I got you beau!

You just need to initialize the pem in the library like this:

```c
#include "http_rest_client.h"

void app_main(void)
{
  char *cert = "----begin certificate--- etc"

  // initialize a cert bundle
  ESP_ERROR_CHECK(http_rest_client_init_cert(cert, strlen(cert)));

  // go about your business...

  // remove the attached bundle ... why? I don't know... but you _can_
  ESP_ERROR_CHECK(http_rest_client_deinit_cert());
}
```

## Do you have a JSON Example?

You Betcha'

You can send that response object directly to cJSON:

```c

#include "http_rest_json_client.h"

void app_main(void)
{

  http_rest_recv_json_t response_buffer = {0};

  // do the request
  ESP_ERROR_CHECK(http_rest_client_get_json("https://rickandmortyapi.com/api/character/1", &response_buffer));

  if (response_buffer.status_code != 200){
    ESP_LOGE(TAG, "an http error occured: %d", response_buffer.status_code);
  }
  else
  {
    char *jsonString = cJSON_Print(response_buffer.json);
    ESP_LOGI(TAG, "Response: %s", jsonString);
    free(jsonString);
  }

  http_rest_client_cleanup_json(&response_buffer);
}

```

What if you want to post json data? yea, it does that too:

```c
#include "http_rest_json_client.h"

void app_main(void)
{

  http_rest_recv_json_t response_buffer = {0};
  cJSON *json_body = cJSON_CreateObject();
  cJSON_AddStringToObject(json_body, "hello", "world");

  // do the request
  ESP_ERROR_CHECK(http_rest_client_get_json("https://httpbin.org/anything", json_body, &response_buffer));

  if (response_buffer.status_code != 200){
    ESP_LOGE(TAG, "an http error occured: %d", response_buffer.status_code);
  }
  else
  {
    char *jsonString = cJSON_Print(response_buffer.json);
    ESP_LOGI(TAG, "Response: %s", jsonString);
    free(jsonString);
    cJSON_Delete(json_body);
  }

  http_rest_client_cleanup_json(&response_buffer);
}

```

# License

MIT License

Copyright (c) 2023 Michael Heijmans

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

readme of http_get_and_parse_json example

                                        
                                        # HTTP REST Client Get and Parse JSON example

This is an example of getting json from the internet and parsing it.

In this example, we create a wifi connection using your ssid and password (edit this in the main.c file)
and then get an https endpoint every second.

# Configure the Project

- open the project config menu (`idf.py menuconfig`)
- Configure the buffers under "HTTP REST Client"

# Build and Flash

```
idf.py -p PORT flash monitor
```

# Example Log Output

```
rst:0x1 (POWERON_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:2
load:0x3fff0030,len:7076
load:0x40078000,len:15576
load:0x40080400,len:4
0x40080400: _init at ??:?

load:0x40080404,len:3876
entry 0x4008064c
I (29) boot: ESP-IDF v5.1 2nd stage bootloader
I (29) boot: compile time Jul 16 2023 18:11:40
I (29) boot: Multicore bootloader
I (33) boot: chip revision: v3.0
I (37) boot.esp32: SPI Speed      : 40MHz
I (42) boot.esp32: SPI Mode       : DIO
I (46) boot.esp32: SPI Flash Size : 16MB
I (51) boot: Enabling RNG early entropy source...
I (56) boot: Partition Table:
I (60) boot: ## Label            Usage          Type ST Offset   Length
I (67) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (75) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (82) boot:  2 factory          factory app      00 00 00010000 00100000
I (90) boot: End of partition table
I (94) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=391e0h (233952) map
I (187) esp_image: segment 1: paddr=00049208 vaddr=3ffb0000 size=03804h ( 14340) load
I (193) esp_image: segment 2: paddr=0004ca14 vaddr=40080000 size=03604h ( 13828) load
I (199) esp_image: segment 3: paddr=00050020 vaddr=400d0020 size=a0f14h (659220) map
I (439) esp_image: segment 4: paddr=000f0f3c vaddr=40083604 size=12030h ( 73776) load
I (481) boot: Loaded app from partition at offset 0x10000
I (481) boot: Disabling RNG early entropy source...
I (492) cpu_start: Multicore app
I (493) cpu_start: Pro cpu up.
I (493) cpu_start: Starting app cpu, entry point is 0x400813a4
0x400813a4: call_start_cpu1 at C:/Users/parab/esp-v5.1/esp-idf/components/esp_system/port/cpu_start.c:154

I (0) cpu_start: App cpu up.
I (511) cpu_start: Pro cpu start user code
I (511) cpu_start: cpu freq: 160000000 Hz
I (511) cpu_start: Application information:
I (515) cpu_start: Project name:     http-client-test
I (521) cpu_start: App version:      1
I (525) cpu_start: Compile time:     Jul 16 2023 18:11:06
I (532) cpu_start: ELF file SHA256:  1a6f2bf9890316e4...
I (538) cpu_start: ESP-IDF:          v5.1
I (542) cpu_start: Min chip rev:     v0.0
I (547) cpu_start: Max chip rev:     v3.99
I (552) cpu_start: Chip rev:         v3.0
I (557) heap_init: Initializing. RAM available for dynamic allocation:
I (564) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (570) heap_init: At 3FFB8448 len 00027BB8 (158 KiB): DRAM
I (576) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (582) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (589) heap_init: At 40095634 len 0000A9CC (42 KiB): IRAM
I (597) spi_flash: detected chip: generic
I (600) spi_flash: flash io: dio
I (605) app_start: Starting scheduler on CPU0
I (609) app_start: Starting scheduler on CPU1
I (609) main_task: Started on CPU0
I (619) main_task: Calling app_main()
I (619) main: Starting app_main...
I (679) wifi:wifi driver task: 3ffc057c, prio:23, stack:6656, core=0
I (709) wifi:wifi firmware version: b2f1f86
I (709) wifi:wifi certification version: v7.0
I (709) wifi:config NVS flash: enabled
I (709) wifi:config nano formating: disabled
I (709) wifi:Init data frame dynamic rx buffer num: 32
I (719) wifi:Init management frame dynamic rx buffer num: 32
I (719) wifi:Init management short buffer num: 32
I (729) wifi:Init dynamic tx buffer num: 32
I (729) wifi:Init static rx buffer size: 1600
I (739) wifi:Init static rx buffer num: 10
I (739) wifi:Init dynamic rx buffer num: 32
I (749) wifi_init: rx ba win: 6
I (749) wifi_init: tcpip mbox: 32
I (749) wifi_init: udp mbox: 6
I (759) wifi_init: tcp mbox: 6
I (759) wifi_init: tcp tx win: 5744
I (759) wifi_init: tcp rx win: 5744
I (769) wifi_init: tcp mss: 1440
I (769) wifi_init: WiFi IRAM OP enabled
I (779) wifi_init: WiFi RX IRAM OP enabled
I (779) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
I (889) wifi:mode : sta (b8:d6:1a:01:e7:a8)
I (889) wifi:enable tsf
I (889) main: Waiting for WiFi connection...
I (889) WIFI_EVENT: WiFi station connecting...
I (909) wifi:new:<4,0>, old:<1,0>, ap:<255,255>, sta:<4,0>, prof:1
I (909) wifi:state: init -> auth (b0)
I (919) wifi:state: auth -> assoc (0)
I (929) wifi:state: assoc -> run (10)
I (959) wifi:connected with <redacted>, aid = 18, channel 4, BW20, bssid = 9a:9e:43:b5:87:70
I (959) wifi:security: WPA2-PSK, phy: bgn, rssi: -62
I (969) wifi:pm start, type: 1

I (969) wifi:AP's beacon interval = 102400 us, DTIM period = 1
I (979) wifi:<ba-add>idx:0 (ifx:0, 9a:9e:43:b5:87:70), tid:0, ssn:0, winSize:64
I (6469) esp_netif_handlers: sta ip: <redacted>, mask: <redacted>, gw: <redacted>
I (6469) main: WiFi connected
I (6469) main: Starting Main Loop...
I (6469) main: Fetching Data from URL: https://jsonplaceholder.typicode.com/todos/1
I (7749) esp-x509-crt-bundle: Certificate validated
I (8879) main: Raw Response string:
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
I (8879) main: Parsed correctly!
I (8879) main: Response JSON:
{
        "userId":       1,
        "id":   1,
        "title":        "delectus aut autem",
        "completed":    false
}
I (8889) main: Looping in 1 sec...
```

                                    

Supports all targets

License: MIT

To add this component to your project, run:

idf.py add-dependency "parabuzzle/http_rest_client^0.1.2"

or download archive

Examples:

http_get_and_parse_json

more details

Stats

  • Downloaded in total
    Downloaded in total 28 times
  • Downloaded this version
    This version: 5 times

Badge

parabuzzle/http_rest_client version: 0.1.2 |