Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heap is running out #2967

Closed
OrakelKSL opened this issue Feb 14, 2017 · 6 comments
Closed

Heap is running out #2967

OrakelKSL opened this issue Feb 14, 2017 · 6 comments
Labels
waiting for feedback Waiting on additional info. If it's not received, the issue may be closed.

Comments

@OrakelKSL
Copy link

Basic Infos

Hardware

Hardware: ESP-12 (NodeMCU V2)
Core Version: 2.3.0

Description

As you can see in serial monitor: If you are tilting your mobile heap is running out quickly.

Settings in IDE

Module: NodeMCU 1.0
Flash Size: 4MB
CPU Frequency: 80Mhz
Flash Mode: ?qio?
Flash Frequency: 40Mhz
Upload Using: OTA / SERIAL
Reset Method: ck / nodemcu

Sketch

/*--------------------------------------------------------------------------------*
   Arduino WiFi-Car
    Manfred Detterbeck February 2017

    Control of WiFi-Car with your smartphone via browser by
    using the orientation of the phone.
    Spec: http://w3c.github.io/deviceorientation/spec-source-orientation.html
    Based on: https://github.com/squix78/esp8266-projects/tree/master/arduino-ide/wifi-car
    Java-script now inline!
    
    Hardware: NodeMVU V2 (caution! V3 doesn't fit on most Motor-shields)
    L293D Motor shield
    Together: https://de.aliexpress.com/wholesale?&SearchText=motor+nodemcu+kit
    2 DC Gear-Motor (1 Left, 2 Right)
  --------------------------------------------------------------------------------
   "THE BEER-WARE LICENSE" (Revision 42):
    <[email protected]> wrote this file. As long as you retain this
    notice you can do whatever you want with this stuff. If we meet some day, and
    you think this stuff is worth it, you can buy me a beer in return
    Manfred Detterbeck
  --------------------------------------------------------------------------------
   You need he following libraries:

   WiFiManager ESP8266 WiFi Connection manager with fallback web configuration
        WiFiManager by tzapu Version 0.12.0
        https://github.com/tzapu/WiFiManager

   -------------------------------------------------------------------------------*/

// IO **** ESP8266 ****
//                      IO-Number ESP8266-12E  Aufdruck  NodeMCU
//                                    1         Rst       RST     Reset-SW: momentary switch NO to GND      / Pullup 10k to 3V3
//                                    2         ADC       AD0     0..1V originqal, over Divider of Nodemcu 0..3,2V
//                                    3         EN        EN      Enable.                                   / Normally connect to 3V3.
#define GPIO16    16  //    16        4         GPIO16    D0      (LED on some Node-MCU. Connect to RST for deep-sleep)
#define GPIO14    14  //    14        5         GPIO14    D5      SPI SCK
#define GPIO12    12  //    12        6         GPIO12    D6      SPI MISO  / (witty cloud: LED green)
#define GPIO13    13  //    13        7         GPIO13    D7      SPI MOSI  / (witty cloud: LED blue)
//                                    8         VCC       3V3     3,3V regulated source, ca. 200mA
//                                    9         CS0       CMD     Not recommended for user. Caution! Internal connection to Flash. Not available for user on all boards
//                                    10        MISO      SD0     Not recommended for user. Caution! Internal connection to Flash. Not available for user on all boards
#define GPIO09    9   //    9         11        GPIO09    SD2     Caution! Only with modification of internal Flash-connections! Not recommended for user. Not available for user on all boards
#define GPIO10    10  //    10        12        GPIO10    SD3     Caution! Only with modification of internal Flash-connections! Not recommended for user. Not available for user on all boards
//                                    13        MOSI      SD1     Not recommended for user. Caution! Internal connection to Flash. Not available for user on all boards
//                                    14        SCLK      CLK     Not recommended for user. Caution! Internal connection to Flash. Not available for user on all boards
//                                    15        GND       GND
#define GPIO15    15  //    15        16        GPIO15    D8      SPI SS    / (witty cloud: LED red)        / Pulldown 10k to GND / LED on some NODE-MCU
#define Mot_R_Fwd 2   //    2         17        GPIO02    D4      Builtin LED (blue) on ESP8266-12 Modul    / Pullup 10k to 3V3
#define Mot_R_Ser 2   //    2         17        GPIO02    D4      Builtin LED (blue) on ESP8266-12 Modul    / Pullup 10k to 3V3
#define Mot_L_Fwd 0   //    0         18        GPIO00    D3      FLASH-SW: momentary switch NO to GND      / Pullup 10k to 3V3
#define Mot_L_Ser 0   //    0         18        GPIO00    D3      FLASH-SW: momentary switch NO to GND      / Pullup 10k to 3V3
#define Mot_R_PWM 4   //    4         19        GPIO04    D2      SDA       / (witty cloud: user-sw)
#define Mot_L_PWM 5   //    5         20        GPIO05    D1      SCL
#define GPIO03    3   //    3         21        RXD0      RX      RXD Serial input.   / Needed for flashing! As IO only momentary switch NO to GND are recommended.
#define GPIO01    1   //    1         22        TXD0      TX      TXD Serial output.  / Needed for flashing! As IO only momentary switch NO to GND are recommended.
// available for User: 11 IO.
// --------------------------------------------------

/*
  L293D   pin   -->   ESP8266 pin   Name
  1,2EN         -->   GPIO05 (D1)   Mot_L_PWM
  3,4EN         -->   GPIO04 (D2)   Mot_R_PWM
  1I,\2I        -->   GPIO00 (D3)   Mot_L_Fwd (\2I to be inverted 1I by Transistor
  3I,\4I        -->   GPIO02 (D4)   Mot_R_Fwd (\4I to be inverted 1I by Transistor
*/

/*
  If you want using 2 esc with PPM + Motor (or modified Servo) instead of LM293D

  Servo R pin   -->   ESP8266 pin   Name
  1 GND   bk    -->   GND
  2 +5V   rd    -->   VIN
  3 Pulse wh    -->   GPIO02 (D4)   Mot_R_Ser

  Servo L pin   -->   ESP8266 pin   Name
  1 GND   bk    -->   GND
  2 +5V   rd    -->   VIN
  3 Pulse wh    -->   GPIO00 (D3)   Mot_L_Ser
*/

#define DEBUG                                 // Debug output over serial

#ifdef DEBUG
#define DEBUG_PRINTF        Serial.printf
#define DEBUG_PRINT         Serial.print
#define DEBUG_PRINTLN       Serial.println
#define DEBUG_WRITE         Serial.write
#else
#define DEBUG_PRINTF(...)
#define DEBUG_PRINT(...)
#define DEBUG_PRINTLN(...)
#define DEBUG_WRITE(...)
#endif

#define SERVO                                         // Use Servo-output for motor / otherwise LM293D
#define NEUTRAL 90

// General settings
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <ESP8266mDNS.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
// Helps with connecting to internet
#include <WiFiManager.h>
#ifdef SERVO
#include <Servo.h>
#endif

/***************************
   General settings
 **************************/
#define FLASH   0             // FLASH-SW for boot-loader 
// Hostname
#define HOSTNAME "WiFi-car-"

/***************************
   Function Prototypes
 **************************/
void configModeCallback (WiFiManager *myWiFiManager);
void ShowSysInfo(void);

/***************************
   Instanzen
 **************************/
//WiFi
// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);
DNSServer dnsServer;
#ifdef SERVO
Servo Servo_R;
Servo Servo_L;
#endif

/***************************
   Global variables
 **************************/
#define STR_BUFFER_SIZE 255                                 // General purpose string buffer
char buffer[STR_BUFFER_SIZE];                               // must be big enough for longest string and the terminating null
unsigned long timeoutMillisMot;                             // Timeout befor Motor stop

/***************************
   Once
 **************************/
void setup() {
#ifdef DEBUG
  Serial.begin(115200);
#endif
  DEBUG_WRITE(27); DEBUG_PRINT("[2J");                    // Teminal command clear screen

  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;

  /**************************************
     Reset Wifi-configuration
   **************************************/
  delay (1000);
  if (!digitalRead(FLASH)) {                                // If FLASH-button is pressed shortly after powering up
    wifiManager.resetSettings();
  }

  // if unconfigured
  wifiManager.setAPCallback(configModeCallback);

  // use this for auto generated name ESP + ChipID
  wifiManager.autoConnect();

  // Setup OTA
  String hostname(HOSTNAME);
  hostname += String(ESP.getChipId(), HEX);
  DEBUG_PRINTLN("OTA-Hostname: " + hostname);
  ArduinoOTA.setHostname((const char *)hostname.c_str());

  ArduinoOTA.onStart([]() {
  });

  ArduinoOTA.onEnd([]() {
  });

  ArduinoOTA.onError([](ota_error_t error) {
    ESP.restart();
  });

  //  ArduinoOTA.onProgress(drawOtaProgress);

  /* setup the OTA server */
  ArduinoOTA.begin();

  // setting Hostname (name for router. Windows don't work but Linux should)
  strcpy  (buffer, hostname.c_str());
  wifi_station_set_hostname( buffer );

  // Start the server
  server.begin();
  DEBUG_PRINTLN("Server started");

  // Print system info
  ShowSysInfo();
  delay (3000);
  DEBUG_WRITE(27); DEBUG_PRINT("[2J");                        // Teminal command clear screen

#ifdef SERVO
  Servo_R.attach(Mot_R_Ser);
  Servo_L.attach(Mot_L_Ser);
  Servo_R.write(NEUTRAL);
  Servo_L.write(NEUTRAL);
#else
  pinMode(Mot_L_PWM, OUTPUT);
  pinMode(Mot_R_PWM, OUTPUT);
  pinMode(Mot_L_Fwd, OUTPUT);
  pinMode(Mot_R_Fwd, OUTPUT);

  digitalWrite(Mot_L_PWM, 0);
  digitalWrite(Mot_R_PWM, 0);

  digitalWrite(Mot_L_Fwd, 1);
  digitalWrite(Mot_R_Fwd, 1);
#endif
}

/***************************
   Cyclic
 **************************/
void loop() {
  unsigned long timeoutMillis;
  String req;
  int motorASpeed;
  int motorBSpeed;
  int motorAForward;
  int motorBForward;
  String parameters;
  int separatorPos;
  int httpPos;
  String leftText;
  String rightText;
  int left;
  int right;

  ArduinoOTA.handle();
  //  dnsServer.processNextRequest();

  WiFiClient client = server.available();
  // Check if a client has connected

  if (!client)   {
    if (millis() > (timeoutMillisMot + 1000)) {
      DEBUG_PRINTLN(F("Motor Aus weil Client ueber 1000ms disconnected war"));
      timeoutMillisMot = millis();

#ifdef SERVO
      Servo_R.write(NEUTRAL);
      Servo_L.write(NEUTRAL);
#else
      analogWrite(Mot_L_PWM, 0);
      analogWrite(Mot_R_PWM, 0);
      digitalWrite(Mot_L_Fwd, 1);
      digitalWrite(Mot_R_Fwd, 1);
#endif
    }
    return;
  }

  if (!client.connected()) {
    DEBUG_PRINTLN("Client disonnected");
    // The client will actually be disconnected
    // when the function returns and 'client' object is detroyed
    client.stop();
    return;
  }


  DEBUG_PRINTLN("new client");
  timeoutMillis = millis();
  while (!client.available()) {
    delay(10);
    if ((millis() - timeoutMillis) > 500) {
      return;                      // Da kommt wohl nix mehr
    }
    yield();
    DEBUG_PRINTLN("Hängt's da?"); // Der ChromGockel hängt da öfters bis zum Timeout. Warum ???
  }


  //  DEBUG_WRITE(27); DEBUG_PRINTLN("[0;0H");                              // Terminal command cursor Home
  DEBUG_WRITE(27); DEBUG_PRINT("[2J");                    // Teminal command clear screen
  DEBUG_PRINT(F("Free Heap Size:\t"));  DEBUG_PRINT(ESP.getFreeHeap());  DEBUG_PRINTLN(F(" Bytes"));

  // Read the first line of the request
  req = client.readStringUntil('\r');
  DEBUG_PRINTLN(req);
  client.flush();

  // Match the request
  motorASpeed = 1023;
  motorBSpeed = 1023;
  motorAForward = 1;
  motorBForward = 1;
  if (req.indexOf("/engines/") != -1) {
    parameters = req.substring(13);
    separatorPos = parameters.indexOf(",");
    httpPos = parameters.indexOf(" HTTP");
    leftText = parameters.substring(0, separatorPos);
    rightText = parameters.substring(separatorPos + 1, httpPos);

    DEBUG_PRINTLN("[" + leftText + "][" + rightText + "]");
    left = leftText.toInt();
    right = rightText.toInt();
    left = parameters.substring(0, separatorPos).toInt();
    right = parameters.substring(separatorPos + 1, httpPos).toInt();
    DEBUG_PRINT(F("Zykluszeit: ")); DEBUG_PRINTLN(millis() - timeoutMillisMot);
    timeoutMillisMot = millis();
#ifdef SERVO
    right = map(right, -1023, 1023, 0, 180);
    Servo_R.write(right);
    left = map(left, -1023, 1023, 0, 180);
    Servo_L.write(left);
#else
    if (left < 0) {
      motorAForward = 0;
    } else {
      motorAForward = 1;
    }
    if (right < 0) {
      motorBForward = 0;
    } else {
      motorBForward = 1;
    }
    analogWrite(Mot_L_PWM, abs(left));
    analogWrite(Mot_R_PWM, abs(right));
    digitalWrite(Mot_L_Fwd, motorAForward);
    digitalWrite(Mot_R_Fwd, motorBForward);
#endif
    yield();

  } else if (req.indexOf("/index.html") != - 1 || req.indexOf("/") != - 1) {
    client.print("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n\
      <html><head></head><body>");
    // Da Java-script integrieren
    //    client.print("<script type='text/javascript' src='http://www.squix.org/blog/smartcar.js'></script>");
    //neu
    client.print("<script type='text/javascript'>\n\
var lastMove = 0;\n\
\n\
function move(left, right) {\n\
  var now = Date.now();\n\
  if (lastMove + 200 < now) {\n\
     lastMove = now; \n\
     var request = new XMLHttpRequest();\n\
     request.open('GET', '/engines/' + Math.round(left) + \",\" + Math.round(right), true);\n\
     request.send(null);\n\
  }\n\
}\n\
\n\
document.onkeydown = function detectKey(event) {\n\
    var e = event.keyCode;\n\
    if (e==87){ move(600, 600);}\n\
    if (e==83){ move(600, -600);}\n\
    if (e==65){ move(-600, 600);}\n\
    if (e==68){ move(-600, -600);}\n\
}\n\
\n\
if (window.DeviceMotionEvent) {\n\
  window.addEventListener('devicemotion', deviceMotionHandler, false);\n\
} else {\n\
  document.getElementById(\"dmEvent\").innerHTML = \"Accelerometer not supported.\"\n\
}\n\
\n\
function deviceMotionHandler(eventData) {\n\
  acceleration = eventData.accelerationIncludingGravity;\n\
  var left = 0;\n\
  var right = 0;\n\
  if (Math.abs(acceleration.y) > 1) {\n\
    var speed = acceleration.y * 123;\n\
    left = Math.min(1023, speed + acceleration.x * 100);\n\
    right = Math.min(1023, speed - acceleration.x * 100);\n\
  } else if (Math.abs(acceleration.x) > 1) {\n\
    var speed = Math.min(1023, Math.abs(acceleration.x) * 123);\n\
    if (acceleration.x > 0) {\n\
      left = speed;\n\
      right = -speed;\n\
    } else {\n\
      left = -speed;\n\
      right = speed;\n\
    }\n\
  }\n\
  if (Math.abs(left) > 100 || Math.abs(right) > 100) {\n\
    move(left, right);\n\
  }\n\
  var direction = \"stop\";\n\
  direction = \"[\" + Math.round(acceleration.x) + \",\" + Math.round(acceleration.y) + \",\" + Math.round(acceleration.z) + \"]<BR/>\" + Math.round(left) + \", \" + Math.round(right);\n\
  document.getElementById(\"vector\").innerHTML =direction;\n\
}\n\
</script>\n");
    //bis hier her

    client.print("<FONT SIZE=2>Smartphone Beschleunigungsvektoren (Neigen L-R, Kippen V-H, Bewegen Auf-Ab): <br />\
      Motor Ansteuerwerte L,R: <br /></FONT>\
      <div id=\"dmEvent\"/>\
      <div id=\"vector\"/>\
      </body></html>");
  }
  yield();

  // Send the response to the client
  client.print("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nGPIO is now </html>\n");
  client.stop();
  yield();
}
/***************************
   Sysinfo
 ***************************/
void ShowSysInfo(void) {
  DEBUG_PRINT(F("Version vom:\t")); DEBUG_PRINT(__DATE__); DEBUG_PRINT(" "); DEBUG_PRINTLN(__TIME__);
  DEBUG_PRINT(F("SDK-Version:\t"));  DEBUG_PRINTLN(ESP.getSdkVersion());
  DEBUG_PRINTLN();
  DEBUG_PRINT(F("ESP8266 Chip-ID:\t"));  DEBUG_PRINTLN(ESP.getChipId());
  DEBUG_PRINT(F("ESP8266 Speed:\t")); DEBUG_PRINT(ESP.getCpuFreqMHz());  DEBUG_PRINTLN(F(" MHz"));
  DEBUG_PRINT(F("Flash Memory:\t"));  DEBUG_PRINT(ESP.getFlashChipSize());  DEBUG_PRINTLN(F(" Bytes"));
  DEBUG_PRINT(F("Free Heap Size:\t"));  DEBUG_PRINT(ESP.getFreeHeap());  DEBUG_PRINTLN(F(" Bytes"));
  DEBUG_PRINTLN();
  DEBUG_PRINT(F("IP-Addresse: "));  DEBUG_PRINTLN(WiFi.localIP());
}

/***************************
  WiFi Manager
 ***************************/
void configModeCallback (WiFiManager *myWiFiManager) {
  DEBUG_PRINTLN("Entered config mode");
  DEBUG_PRINTLN(WiFi.softAPIP());
  //if you used auto generated SSID, print it
  DEBUG_PRINTLN(myWiFiManager->getConfigPortalSSID());
}

Debug Messages

Heap started with 40192 and runs down to:

�[2JFree Heap Size: 272 Bytes
GET /engines/285,371 HTTP/1.1
[285][371]
Zykluszeit: 216

Exception (28):
epc1=0x4020498f epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000008 depc=0x00000000

ctx: sys
sp: 3ffff990 end: 3fffffb0 offset: 01a0

stack>>>
3ffffb30: 0000000c 00000000 3fff2194 40204985

@OrakelKSL
Copy link
Author

I made some investigations:
Every time client receives a request, the heap goes down and does NOT recover!
You can easily test it: Connect with any browser and keep F5 pressed: Heap goes rapidly down to a few bytes and does never recover.

Check the following very simple sketch:

#include <ESP8266WiFi.h>

const char* ssid = S_SSID;
const char* password = S_PASSWORD;

// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);

void setup() {
  Serial.begin(115200);
  
  // Connect to WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  
  // Start the server
  server.begin();
  Serial.println("Server started");

  // Print the IP address
  Serial.println(WiFi.localIP());
}


void loop() {
    Serial.write(27);                                           //Print "esc"
    Serial.print("[0;0H");                                      // Terminal command cursor Home
    //    Serial.print("[2J");                                  // Terminal command clear-screen
  Serial.print(F("Free Heap Size:\t"));  Serial.print(ESP.getFreeHeap());  Serial.println(F(" Bytes"));

  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
  
  // Wait until the client sends some data
  Serial.println("new client");
  while(!client.available()){
    delay(1);
  }
  
  // Read the first line of the request
  String req = client.readStringUntil('\r');
  Serial.println(req);
  client.flush();
}

@OrakelKSL
Copy link
Author

...ir recovers, but it takes MINUTES!

@OrakelKSL
Copy link
Author

... and if you keep f5 pressed exception 28 or 29 occours.
Has anybody suggestions to force "free the heap" at end of loop?
:-(

„Nihil est in ESP, quod non fuerit in intellectu" - Nichts ist im ESP, was zuvor nicht im Verstand war.

@devyte
Copy link
Collaborator

devyte commented Sep 9, 2017

@OrakelKSL is this issu still valid?
The fact heap would recover hints at sockets being kept in TIME_WAIT state, probably for a the typical 2 minutes. That is per RFC (I forget which), although for tiny embedded systems implementations usually don't hold to it.
Still, with latest git I don't currently see the issue when refreshing a webpage.

@devyte devyte added the waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. label Sep 9, 2017
@devyte
Copy link
Collaborator

devyte commented Oct 10, 2017

Old issue and no feedback in over a month => closing.

@efeuentertainment
Copy link

there is a workaround for running out of heap due to sockets kept in TIME_WAIT.
issue #1923

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting for feedback Waiting on additional info. If it's not received, the issue may be closed.
Projects
None yet
Development

No branches or pull requests

3 participants