Board: ESP32 Dev Kit board
Core Installation version: Using version 1.0.4 of the ESP32 board with Arduino IDE (also tried 1.0.0 and 1.0.1, 1.0.0 didn't allow for an easy setting of partitions, 1.0.1 exhibited the same issue).
IDE name: Arduino IDE (via VScode extension)
Flash Frequency: 80Mhz
PSRAM enabled: no
Upload Speed: 921600
Computer OS: Manjaro Linux 5.3.x
Partition scheme is set to huge app (3MB flash, no OTA).
I'm trying to poll a HTTPS URL while scanning some BLE devices. The issue is that after 3-6 successful HTTP calls, I constantly keep getting a "connection refused" error from the HTTP client.
I don't even need to start scanning for BLE, I just need to init the BLEDevice. Removing the init call makes requests succesful 100% of the time (tested while running for 5 minutes).
I've created a minimal file outlining how the issue is replicated. I also tried initializing the BLEDevice object in a separate task on core 0, however that only made the "connection refused" messages to show up more quickly (after 1 call usually).
Code is below, based on the BLE and WifiClientSecure examples:
#include <Adafruit_NeoPixel.h>
#include <BLEDevice.h>
#include <HTTPClient.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "secrets.h"
const char* rootCACertificate = \
"-----BEGIN CERTIFICATE-----\n" \
"ROOTCACERT" \
"-----END CERTIFICATE-----\n";
void setClock() {
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
Serial.print(F("Waiting for NTP time sync: "));
time_t nowSecs = time(nullptr);
while (nowSecs < 8 * 3600 * 2) {
delay(500);
Serial.print(F("."));
yield();
nowSecs = time(nullptr);
}
Serial.println();
struct tm timeinfo;
gmtime_r(&nowSecs, &timeinfo);
Serial.print(F("Current time: "));
Serial.print(asctime(&timeinfo));
}
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
esp_log_level_set("*", ESP_LOG_VERBOSE);
Serial.println("Starting Arduino BLE Client application...");
Serial.print("setup() running on core ");
Serial.println(xPortGetCoreID());
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
// wait for WiFi connection
Serial.print("Waiting for WiFi to connect...");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Connecting to WiFi..");
}
Serial.println(" connected");
setClock();
BLEDevice::init("");
} // End of setup.
// This is the Arduino main loop function.
void loop() {
Serial.print("loop() running on core ");
Serial.println(xPortGetCoreID());
WiFiClientSecure *client = new WiFiClientSecure;
if(client) {
client -> setCACert(rootCACertificate);
{
// Add a scoping block for HTTPClient https to make sure it is destroyed before WiFiClientSecure *client is
HTTPClient https;
Serial.println(WiFi.localIP());
Serial.print("[HTTPS] begin...\n");
if (https.begin(*client, "<AN_HTTPS_URL>")) { // HTTPS
Serial.print("[HTTPS] GET...\n");
https.addHeader("Authorization", "Bearer <just_an_auth_header>");
https.addHeader("Content-Type","application/json");
https.addHeader("Connection","close");
// start connection and send HTTP header
int httpCode = https.GET();
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been send and Server response header has been handled
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
String payload = https.getString();
Serial.println(payload);
}
} else {
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
}
https.end();
} else {
Serial.printf("[HTTPS] Unable to connect\n");
}
// End extra scoping block
}
delete client;
} else {
Serial.println("Unable to create client");
}
Serial.println();
Serial.println("Waiting 2s before the next round...");
delay(2000);
} // End of loop
Sometimes I'm seeing debug some messages like the ones below before connections are refused, but that's not the case 100% of the time. However, the correct behaviour of the requests are always returned once the BLEDevice::init() call is commented out.
````
I (9589) wifi: bcn_timout,ap_probe_send_start
I (12091) wifi: ap_probe_send over, resett wifi status to disassoc
I (12091) wifi: state: run -> init (c800)
I (12091) wifi: pm stop, total sleep time: 2330314 us / 11721780 us
I (12096) wifi: n:11 0, o:11 0, ap:255 255, sta:11 0, prof:1
[HTTPS] GET... failed, error: connection refused
````
````
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:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
Starting Arduino BLE Client application...
setup() running on core 1
I (34) wifi: wifi driver task: 3ffd16f0, prio:23, stack:3584, core=0
I (113) wifi: wifi firmware version: 7997e4b
I (114) wifi: config NVS flash: enabled
I (114) wifi: config nano formating: disabled
I (114) wifi: Init dynamic tx buffer num: 32
I (118) wifi: Init data frame dynamic rx buffer num: 32
I (123) wifi: Init management frame dynamic rx buffer num: 32
I (128) wifi: Init management short buffer num: 32
I (133) wifi: Init static rx buffer size: 1600
I (137) wifi: Init static rx buffer num: 16
I (141) wifi: Init dynamic rx buffer num: 32
I (211) wifi: mode : sta (4c:11:ae:76:30:48)
Waiting for WiFi to connect...I (333) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:1
I (333) wifi: state: init -> auth (b0)
I (336) wifi: state: auth -> assoc (0)
I (341) wifi: state: assoc -> run (10)
I (368) wifi: connected with
I (372) wifi: pm start, type: 1
Connecting to WiFi..
connected
Waiting for NTP time sync: .
Current time: Tue Nov 5 11:50:15 2019
loop() running on core 1
192.168.1.9
[HTTPS] begin...
[HTTPS] GET...
[HTTPS] GET... code: 200
{"attributes": {"friendly_name": "airly_caqi"}, "context": {"id": "a9e6aeae4b4e4ddcac9c39def566e315", "parent_id": null, "user_id": null}, "entity_id": "sensor.airly_caqi", "last_changed": "2019-11-05T11:18:48.582908+00:00", "last_updated": "2019-11-05T11:18:48.582908+00:00", "state": "12"}
Waiting 2s before the next round...
loop() running on core 1
192.168.1.9
[HTTPS] begin...
[HTTPS] GET...
[HTTPS] GET... code: 200
{"attributes": {"friendly_name": "airly_caqi"}, "context": {"id": "a9e6aeae4b4e4ddcac9c39def566e315", "parent_id": null, "user_id": null}, "entity_id": "sensor.airly_caqi", "last_changed": "2019-11-05T11:18:48.582908+00:00", "last_updated": "2019-11-05T11:18:48.582908+00:00", "state": "12"}
Waiting 2s before the next round...
loop() running on core 1
192.168.1.9
[HTTPS] begin...
[HTTPS] GET...
[HTTPS] GET... code: 200
{"attributes": {"friendly_name": "airly_caqi"}, "context": {"id": "a9e6aeae4b4e4ddcac9c39def566e315", "parent_id": null, "user_id": null}, "entity_id": "sensor.airly_caqi", "last_changed": "2019-11-05T11:18:48.582908+00:00", "last_updated": "2019-11-05T11:18:48.582908+00:00", "state": "12"}
Waiting 2s before the next round...
loop() running on core 1
192.168.1.9
[HTTPS] begin...
[HTTPS] GET...
[HTTPS] GET... code: 200
{"attributes": {"friendly_name": "airly_caqi"}, "context": {"id": "a9e6aeae4b4e4ddcac9c39def566e315", "parent_id": null, "user_id": null}, "entity_id": "sensor.airly_caqi", "last_changed": "2019-11-05T11:18:48.582908+00:00", "last_updated": "2019-11-05T11:18:48.582908+00:00", "state": "12"}
Waiting 2s before the next round...
loop() running on core 1
192.168.1.9
[HTTPS] begin...
[HTTPS] GET...
[HTTPS] GET... code: 200
{"attributes": {"friendly_name": "airly_caqi"}, "context": {"id": "a9e6aeae4b4e4ddcac9c39def566e315", "parent_id": null, "user_id": null}, "entity_id": "sensor.airly_caqi", "last_changed": "2019-11-05T11:18:48.582908+00:00", "last_updated": "2019-11-05T11:18:48.582908+00:00", "state": "12"}
Waiting 2s before the next round...
loop() running on core 1
192.168.1.9
[HTTPS] begin...
[HTTPS] GET...
[HTTPS] GET... failed, error: connection refused
Waiting 2s before the next round...
loop() running on core 1
192.168.1.9
[HTTPS] begin...
[HTTPS] GET...
[HTTPS] GET... failed, error: connection refused
Waiting 2s before the next round...
loop() running on core 1
192.168.1.9
[HTTPS] begin...
[HTTPS] GET...
[HTTPS] GET... failed, error: connection refused
Waiting 2s before the next round...
loop() running on core 1
192.168.1.9
[HTTPS] begin...
[HTTPS] GET...
[HTTPS] GET... failed, error: connection refused
```
After figuring out that I should have enabled verbose logging from the compilation options, I was able to see a complete log of how the request is done. Seems that there's a memory allocation issue:
````
[V][ssl_client.cpp:56] start_ssl_client(): Free internal heap before TLS 87232
[V][ssl_client.cpp:58] start_ssl_client(): Starting socket
[V][ssl_client.cpp:93] start_ssl_client(): Seeding the random number generator
[V][ssl_client.cpp:102] start_ssl_client(): Setting up the SSL/TLS structure...
[V][ssl_client.cpp:115] start_ssl_client(): Loading CA cert
[V][ssl_client.cpp:180] start_ssl_client(): Setting hostname for TLS session...
[V][ssl_client.cpp:195] start_ssl_client(): Performing the SSL/TLS handshake...
[E][WiFiClientSecure.cpp:132] connect(): start_ssl_client: -16
[V][ssl_client.cpp:248] stop_ssl_socket(): Cleaning SSL connection.
[D][HTTPClient.cpp:1018] connect(): failed connect to
[W][HTTPClient.cpp:1318] returnError(): error(-1): connection refused
e[D][HTTPClient.cpp:383] disconnect(): tcp is closed
[V][ssl_client.cpp:248] stop_ssl_socket(): Cleaning SSL connection.
[V][ssl_client.cpp:248] stop_ssl_socket(): Cleaning SSL connection.
````
BIGNUM - Memory allocation failed
There is not enough memory to Performing the SSL/TLS handshake... and error(-1): connection refused.
hi, the BT stack with wifi connectivity is too bulky, it cant fit in the 4mb module. you have to buy a 16mb flash module. or you have to set up the partition in the 4mb module so that these 2 can be fit accordingly in this 4mb module.
@sachinrana2k11 thanks. I actually already ran into that problem and modified the partition scheme so that it allocates 3MB of flash for program memory. This solved the issue with storage, but got me into the RAM issues above.
@chegewara I've seen the memory allocation failed message, however given that some requests succeed at the beginning of each boot, I'm wondering if we're not also facing a memory leak here. Is there any good reason why this would work only 4-6 times before running out of memory?
Additionally to the memory leak, would the only fix for having both ble and https functions be to have external PSRAM? I'd have thought it would be a pretty common scenario with an ESP32.
Try to add logging to track memory in app.
[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.
[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.
I have run into this issue and have a work around. The issue is that BLE device uses a lot of heap space, so later calls to ssl_connect (and/or other ssl routines) do not have enough heap space. The workaround I first tried should have worked but didn't. Essentially I call BLEDevice::deinit(false); write before I try to make my https request. This actually works for the https (ssl_connect) request but even though I immediately call BLEDevice::init(""); to reinitialize the BLEDevice, future scans don't find any BLE device. The total workaround that works properly is right before I call the deinit I actually release the memory associated with Bluetooth classis. I do that by esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT). For some reason when I do this the https works, and when I then do the init for BLE, the scans work. So the code looks something like this with print statements to see heap
Serial.printf("\n****Before BLEDevice::deinit ESP.getFreeHeap() %u\n",ESP.getFreeHeap());
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
BLEDevice::deinit(false);
Serial.printf("\n****After BLEDevice::deinit ESP.getFreeHeap() %u\n",ESP.getFreeHeap());
Then call https and then after done with ssl
if (!(BLEDevice::getInitialized())){
Serial.printf("Calling BLEDevice::init\n");
BLEDevice::init("");
initializeBLEScan(pBLEScan);
}
Note in my case I didn't need classic bluetooth, so it works for me. Bottom line the underlying issue is running out of heap space. Ideally init/deinit sequence would work but release of classic bluetooth memory seems help the init/deinit sequence. Hope this gives more clarity. The easy way to reproduce is just watch heap size when making https request when BLE is init'ed and then when not..
I have a solution running BLE, HTTPS, SPIFSS, and OTA on one standard 4Mb ESP32-WROOM-32. But I do the following:
BLEDevice::deinit(false); first.deepsleep, which takes care of memory leaks, etc.Before the BLEDevice::deinit(false); I had the same BIGNUM problem. I hope this add a bit insight...
Most helpful comment
I have run into this issue and have a work around. The issue is that BLE device uses a lot of heap space, so later calls to ssl_connect (and/or other ssl routines) do not have enough heap space. The workaround I first tried should have worked but didn't. Essentially I call BLEDevice::deinit(false); write before I try to make my https request. This actually works for the https (ssl_connect) request but even though I immediately call BLEDevice::init(""); to reinitialize the BLEDevice, future scans don't find any BLE device. The total workaround that works properly is right before I call the deinit I actually release the memory associated with Bluetooth classis. I do that by esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT). For some reason when I do this the https works, and when I then do the init for BLE, the scans work. So the code looks something like this with print statements to see heap
Then call https and then after done with ssl
if (!(BLEDevice::getInitialized())){
Serial.printf("Calling BLEDevice::init\n");
BLEDevice::init("");
initializeBLEScan(pBLEScan);
}
Note in my case I didn't need classic bluetooth, so it works for me. Bottom line the underlying issue is running out of heap space. Ideally init/deinit sequence would work but release of classic bluetooth memory seems help the init/deinit sequence. Hope this gives more clarity. The easy way to reproduce is just watch heap size when making https request when BLE is init'ed and then when not..