英文:
Calling a routine on ESP8266 decreases progressively the Free Heap
问题
I've translated the code snippet you provided into Chinese for you:
我正在使用一个名为printIPs的例程,在串口和显示器上打印所有连接到用作NAT的ESP8266的站点的IP的最后一个字节。
整个程序在不调用该例程时正常工作。但如果调用它,我会得到渐进式的内存堆减少,导致“esp Fatal exception 29(StoreProhibitedCause)”错误。
我猜想我应该更好地分配这些结构以避免内存碎片。
以下是该例程的代码:
```cpp
void printIPs() {
// 这部分用于列出连接的站点的最后一个字节。
unsigned char softap_stations_cnt = 0;
struct station_info *stat_info;
struct ip4_addr *IPaddress;
uint32 uintaddress;
byte mordisco;
// 这部分用于列出连接的站点的最后一个字节。
softap_stations_cnt = wifi_softap_get_station_num(); // 连接到ESP8266软接入点的站点数量
stat_info = wifi_softap_get_station_info();
if (stat_info != NULL) {
Serial.print("查找连接的站点IP数量:");
Serial.println(softap_stations_cnt);
Serial.println("打印连接的站点IP的最后一个字节:");
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);
}
}
}
虽然我认为问题可能仅通过查看这个例程就可以解决,但为了完整起见,以下是整个程序的代码。
(你的完整代码)
这是关于异常的部分序列输出:
23:19:32.857 -> C=300 - 剩余堆内存: 712
23:19:32.857 -> 连接的站点数量: 2
23:19:32.857 -> 打印连接的站点IP的最后一个字节:
23:19:32.857 -> 97 98
23:19:34.848 -> C=301 - 剩余堆内存: 664
23:19:34.848 -> 连接的站点数量: 2
23:19:34.848 -> 打印连接的站点IP的最后一个字节:
23:19:34.881 -> 97 98
23:19:36.873 -> C=302 - 剩余堆内存: 616
23:19:36.873 -> 连接的站点数量: 2
23:19:36.873 -> 打印连接的站点IP的最后一个字节:
23:19:36.873 -> 97 98
23:19:38.896 -> C=303 - 剩余堆内存: 440
23:19:38.896 -> 连接的站点数量: 2
23:19:38.896 -> 打印连接的站点IP的最后一个字节:
23:19:38.896 -> 97 98
23:19:40.920 -> C=304 - 剩余堆内存: 520
23:19:40.920 -> 连接的站点数量: 2
23:19:40.920 -> 打印连接的站点IP的最后一个字节:
23:19:40.920 -> 97 98
23:19:40.986 -> 致命异常 29(StoreProhibitedCause):
23:19:40.986 -> epc1=0x4000df64, epc2=0x00000000, epc3=0x00000000, excvaddr=0x00000000, depc=0x00000000
即使相同的代码在主循环中,并且变量在主头文件中声明,我也遇到了相同的问题。
我注意到,最佳情况下,堆内存的减少是缓慢进行的。它取决于连接的站点数量以及它们的互联网使用情况。
请注意,如果您需要进一步的帮助或解决这个问题,您可能需要进行内存分析和调试,以找出导致内存问题的根本原因。
<details>
<summary>英文:</summary>
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.
</details>
# 答案1
**得分**: 1
你的程序存在一个“内存泄漏”,它在分配内存或调用分配内存的函数时,没有释放它。
你已经找到了泄漏内存的函数;从这里开始调试的一个好方法是从该函数中删除代码行,直到你发现堆不再收缩,或者在每行代码后打印堆的大小,以找到负责分配的那行代码。
在这种情况下,你会发现调用 `wifi_softap_get_station_info()` 分配了内存,但从未释放。
在你完成后,你需要调用 `wifi_softap_free_station_info()` - 请确保在你的函数 `printIPs()` 返回之前调用它。
调用 `wifi_softap_free_station_info()` 很重要,而不只是在 `wifi_softap_get_station_info()` 返回的内容上调用 `free()`,以避免你的代码对于 `wifi_softap_get_station_info()` 如何管理内存作出假设。
`wifi_softap_get_station_info()` 和 `wifi_softap_free_station_info()` 都是 ESP8266 Arduino 框架所基于的底层 SDK 的一部分,遗憾的是它们的文档比 Arduino 函数少。
<details>
<summary>英文:</summary>
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.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论