I am using a routine printIPs to print on serial and on a display the last byte of the IP of all connected stations to a ESP8266 used as NAT. The whole schetch works if I do not call that routine. If I do call it, I get a progressive free heap reduction that leads to "esp Fatal exception 29(StoreProhibitedCause)". I guess that I should allocate better the structures to avoid memory fragmentation. Here is the routune:
void printIPs() {
//This part is to list the last byte of the connected stations.
unsigned char softap_stations_cnt = 0;
struct station_info *stat_info;
struct ip4_addr *IPaddress;
uint32 uintaddress;
byte mordisco;
//This part is to list the last byte of the connected stations.
softap_stations_cnt = wifi_softap_get_station_num(); // Count of stations which are connected to ESP8266 soft-AP
stat_info = wifi_softap_get_station_info();
if (stat_info != NULL) {
Serial.print("Finding number of Station IPs connected: ");
Serial.println(softap_stations_cnt);
Serial.println("Printing the last byte of the Station IPs connected:");
display.print(" - IPs: ");
while (stat_info != NULL) {
IPaddress = &stat_info->ip;
uintaddress = IPaddress->addr;
mordisco = (uintaddress >> 24);
Serial.print(mordisco);
Serial.print(" ");
display.print(mordisco);
display.print(" ");
stat_info = STAILQ_NEXT(stat_info, next);
}
}
}
Although I think the issue can be solved looking at the routine only, for completiness, here is the whole schetch:
// NAPT example released to public domain
#if LWIP_FEATURES && !LWIP_IPV6
#define HAVE_NETDUMP 0
#include <ESP8266WiFi.h>
#include <lwip/napt.h>
#include <lwip/dns.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
extern "C" {
#include "user_interface.h"
#include "wpa2_enterprise.h"
#include "c_types.h"
}
// SSID to connect to
char ssid[] = "SSIDNAME";
char username[] = "";
char identity[] = "";
char password[] = "";
String ExtenderSSID = "OTHERSSID";
String ExtenderPW = "";
int c = 0;
uint8_t target_esp_mac[6] = {0x24, 0x0a, 0xc4, 0x9a, 0x58, 0x28};
#define NAPT 1000
#define NAPT_PORT 10
#if HAVE_NETDUMP
#include <NetDump.h>
void dump(int netif_idx, const char* data, size_t len, int out, int success) {
(void)success;
Serial.print(out ? F("out ") : F(" in "));
Serial.printf("%d ", netif_idx);
// optional filter example: if (netDump_is_ARP(data))
{
netDump(Serial, data, len);
// netDumpHex(Serial, data, len);
}
}
#endif
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
Wire.setClock(100000);
Wire.begin(5, 4); //SDA, SCL
// by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x32)
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.print("Heap on start: ");
display.println(ESP.getFreeHeap());
display.println("Creating Connection info for Station WiFi...");
display.display();
delay(2000);
// init oled done
Serial.printf("\n\nNAPT Range extender\n");
Serial.printf("Heap on start: %d\n", ESP.getFreeHeap());
#if HAVE_NETDUMP
phy_capture = dump;
#endif
// first, connect to STA so we can get a proper local DNS server
WiFi.mode(WIFI_STA);
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.println("Created.");
display.print("SDK version: ");
display.println(system_get_sdk_version());
display.print("Free Heap: ");
display.println(ESP.getFreeHeap());
display.println("Creating Station...");
display.display();
delay(2000);
Serial.printf("SDK version: %s\n", system_get_sdk_version());
Serial.printf("Free Heap: %4d\n", ESP.getFreeHeap());
// Setting ESP into STATION mode only (no AP mode or dual mode)
wifi_set_opmode(STATION_MODE);
struct station_config wifi_config;
memset(&wifi_config, 0, sizeof(wifi_config));
strcpy((char*)wifi_config.ssid, ssid);
strcpy((char*)wifi_config.password, password);
wifi_station_set_config(&wifi_config);
wifi_set_macaddr(STATION_IF, target_esp_mac);
wifi_station_set_wpa2_enterprise_auth(1);
// Clean up to be sure no old data is still inside
wifi_station_clear_cert_key();
wifi_station_clear_enterprise_ca_cert();
wifi_station_clear_enterprise_identity();
wifi_station_clear_enterprise_username();
wifi_station_clear_enterprise_password();
wifi_station_clear_enterprise_new_password();
wifi_station_set_enterprise_identity((uint8*)identity, strlen(identity));
wifi_station_set_enterprise_username((uint8*)username, strlen(username));
wifi_station_set_enterprise_password((uint8*)password, strlen((char*)password));
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.print("Waiting to get link to ");
display.print(ssid);
display.println("...");
display.print("SDK version: ");
display.println(system_get_sdk_version());
display.print("Free Heap: ");
display.println(ESP.getFreeHeap());
display.display();
delay(2000);
wifi_station_connect();
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.println("Connected!");
display.print("IP address: ");
display.println(WiFi.localIP());
display.print("Free Heap: ");
display.println(String(ESP.getFreeHeap()));
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
display.print("DNS1: ");
display.println(WiFi.dnsIP(0).toString());
display.print("DNS2: ");
display.println(WiFi.dnsIP(1).toString());
display.display();
delay(2000);
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(500);
}
Serial.printf("\nSTA: %s (dns: %s / %s)\n", WiFi.localIP().toString().c_str(), WiFi.dnsIP(0).toString().c_str(), WiFi.dnsIP(1).toString().c_str());
// By default, DNS option will point to the interface IP
// Instead, point it to the real DNS server.
// Notice that:
// - DhcpServer class only supports IPv4
// - Only a single IP can be set
auto& server = WiFi.softAPDhcpServer();
server.setDns(WiFi.dnsIP(0));
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.println("Creating AP... ");
display.print("Free Heap: ");
display.println(String(ESP.getFreeHeap()));
display.display();
delay(2000);
WiFi.softAPConfig( // enable AP, with android-compatible google domain
IPAddress(172, 217, 28, 254), IPAddress(172, 217, 28, 254), IPAddress(255, 255, 255, 0));
WiFi.softAP(ExtenderSSID, ExtenderPW);
Serial.printf("AP: %s\n", WiFi.softAPIP().toString().c_str());
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.print("AP Created: ");
display.println(WiFi.softAPIP().toString().c_str());
display.print("Free Heap: ");
display.println(String(ESP.getFreeHeap()));
display.display();
delay(2000);
Serial.printf("Heap before: %d\n", ESP.getFreeHeap());
err_t ret = ip_napt_init(NAPT, NAPT_PORT);
Serial.printf("ip_napt_init(%d,%d): ret=%d (OK=%d)\n", NAPT, NAPT_PORT, (int)ret, (int)ERR_OK);
if (ret == ERR_OK) {
ret = ip_napt_enable_no(SOFTAP_IF, 1);
Serial.printf("ip_napt_enable_no(SOFTAP_IF): ret=%d (OK=%d)\n", (int)ret, (int)ERR_OK);
if (ret == ERR_OK) {
Serial.printf("WiFi Network '%s' with defined password is now NATed behind '%s'\n", ExtenderSSID, ssid);
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.print("WiFi Network ");
display.println(ExtenderSSID);
display.print("is NATed behind ");
display.println(ssid);
display.print("Free Heap: ");
display.println(String(ESP.getFreeHeap()));
display.display();
}
}
Serial.printf("Heap after napt init: %d\n", ESP.getFreeHeap());
if (ret != ERR_OK) {
Serial.printf("NAT initialization failed\n");
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.println("NAT initialization failed");
display.display();
}
delay(2000);
}
#else
void setup() {
Serial.begin(115200);
Serial.printf("\n\nNAPT not supported in this configuration\n");
}
#endif
void printIPs() {
//This part is to list the last byte of the connected stations.
unsigned char softap_stations_cnt = 0;
struct station_info *stat_info;
struct ip4_addr *IPaddress;
uint32 uintaddress;
byte mordisco;
//This part is to list the last byte of the connected stations.
softap_stations_cnt = wifi_softap_get_station_num(); // Count of stations which are connected to ESP8266 soft-AP
stat_info = wifi_softap_get_station_info();
if (stat_info != NULL) {
Serial.print("Finding number of Station IPs connected: ");
Serial.println(softap_stations_cnt);
Serial.println("Printing the last byte of the Station IPs connected:");
display.print(" - IPs: ");
while (stat_info != NULL) {
IPaddress = &stat_info->ip;
uintaddress = IPaddress->addr;
mordisco = (uintaddress >> 24);
Serial.print(mordisco);
Serial.print(" ");
display.print(mordisco);
display.print(" ");
stat_info = STAILQ_NEXT(stat_info, next);
}
}
}
void loop() {
delay(2000);
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.print("FH (");
display.print(c);
c = c + 1;
if (c > 999) {
c = 0;
}
Serial.print("C=" + String(c));
display.print("): ");
display.println(String(ESP.getFreeHeap()));
Serial.println(" - Free Heap: " + String(ESP.getFreeHeap()));
display.print("AP: ");
display.println(WiFi.softAPIP().toString().c_str());
display.print("Stations N: ");
display.print(WiFi.softAPgetStationNum());
Serial.print("Number of stations connected: ");
Serial.println(WiFi.softAPgetStationNum());
yield();
printIPs();
Serial.println("");
display.display();
yield();
}
Here a part of the serial output aroung the exception:
23:19:32.857 -> C=300 - Free Heap: 712
23:19:32.857 -> Number of stations connected: 2
23:19:32.857 -> Printing the last byte of the Station IPs connected:
23:19:32.857 -> 97 98
23:19:34.848 -> C=301 - Free Heap: 664
23:19:34.848 -> Number of stations connected: 2
23:19:34.848 -> Printing the last byte of the Station IPs connected:
23:19:34.881 -> 97 98
23:19:36.873 -> C=302 - Free Heap: 616
23:19:36.873 -> Number of stations connected: 2
23:19:36.873 -> Printing the last byte of the Station IPs connected:
23:19:36.873 -> 97 98
23:19:38.896 -> C=303 - Free Heap: 440
23:19:38.896 -> Number of stations connected: 2
23:19:38.896 -> Printing the last byte of the Station IPs connected:
23:19:38.896 -> 97 98
23:19:40.920 -> C=304 - Free Heap: 520
23:19:40.920 -> Number of stations connected: 2
23:19:40.920 -> Printing the last byte of the Station IPs connected:
23:19:40.920 -> 97 98
23:19:40.986 -> Fatal exception 29(StoreProhibitedCause):
23:19:40.986 -> epc1=0x4000df64, epc2=0x00000000, epc3=0x00000000, excvaddr=0x00000000, depc=0x00000000
I had the same issue even when the same code was in the main loop and the variable declatation on the main header. I see that the free heap reduction, on the best case, is slowly progressive. It depends on the number of connected stations and their internet usage.
Your program has a "memory leak" and is allocating memory or calling a function that allocates memory, without ever freeing it.
You already isolated the function that's leaking memory; a good way to debug it from here is to remove lines from the function until you find that the heap is no longer shrinking or to print the size of the heap after each line of code in order to find the line that's responsible for the allocation.
In this case you'd find that the call to wifi_softap_get_station_info()
is allocating memory that's never being freed.
You need to call wifi_softap_free_station_info()
once you're done - be sure to call this before your function printIPs()
returns.
It's important to call wifi_softap_free_station_info()
rather than just calling free()
on whatever wifi_softap_get_station_info()
returns so that your code doesn't make assumptions about how wifi_softap_get_station_info()
manages its memory.
Both wifi_softap_get_station_info()
and wifi_softap_free_station_info()
are part of the underlying SDK on which the ESP8266 Arduino framework is built and unfortunately are less well-documented than the Arduino functions.