1
0
mirror of https://github.com/bdring/Grbl_Esp32.git synced 2025-08-29 17:19:50 +02:00

Use less memory (#644)

a) closeFile() now does SD.end() to release memory
after running a file from SD.
b) Several task stacks are smaller
c) All tasks now check their free space if DEBUG_REPORT_STACK_FREE
is defined.  platformio.ini has a commented-out line that can be
uncommented to turn that on.
d) Similarly, platformio.ini can turn on DEBUG_REPORT_HEAP_SIZE
e) Fixed a small leak that occurred when listing local files.

With these changes, the heap size tends to hover around 53K, dropping
to about 37K when running a file from SD.
This commit is contained in:
Mitch Bradley
2020-10-16 14:13:21 -10:00
committed by GitHub
parent ab36c5a86d
commit b72cd4c727
13 changed files with 221 additions and 175 deletions

View File

@@ -42,6 +42,14 @@
*/ */
#include "Config.h" #include "Config.h"
// This block of #includes is necessary for Report.h
#include "Error.h"
#include "WebUI/Authentication.h"
#include "WebUI/ESPResponse.h"
#include "Probe.h"
#include "System.h"
#include "Report.h"
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <driver/periph_ctrl.h> #include <driver/periph_ctrl.h>
#include <rom/lldesc.h> #include <rom/lldesc.h>
@@ -516,6 +524,9 @@ static void IRAM_ATTR i2sOutTask(void* parameter) {
o_dma.rw_pos = 0; // If someone calls i2s_out_push_sample, make sure there is no buffer overflow o_dma.rw_pos = 0; // If someone calls i2s_out_push_sample, make sure there is no buffer overflow
} }
I2S_OUT_PULSER_EXIT_CRITICAL(); // Unlock pulser status I2S_OUT_PULSER_EXIT_CRITICAL(); // Unlock pulser status
static UBaseType_t uxHighWaterMark = 0;
reportTaskStackSize(uxHighWaterMark);
} }
} }
#endif #endif
@@ -907,7 +918,7 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) {
// Create the task that will feed the buffer // Create the task that will feed the buffer
xTaskCreatePinnedToCore(i2sOutTask, xTaskCreatePinnedToCore(i2sOutTask,
"I2SOutTask", "I2SOutTask",
1024 * 10, 4096,
NULL, NULL,
1, 1,
nullptr, nullptr,

View File

@@ -330,11 +330,8 @@ void limits_init() {
} }
if (limit_sw_queue == NULL) { if (limit_sw_queue == NULL) {
grbl_msg_sendf(CLIENT_SERIAL, grbl_msg_sendf(
MsgLevel::Info, CLIENT_SERIAL, MsgLevel::Info, "%s limit switch on pin %s", reportAxisNameMsg(axis, gang_index), pinName(pin).c_str());
"%s limit switch on pin %s",
reportAxisNameMsg(axis, gang_index),
pinName(pin).c_str());
} }
} }
} }
@@ -427,6 +424,8 @@ void limitCheckTask(void* pvParameters) {
mc_reset(); // Initiate system kill. mc_reset(); // Initiate system kill.
sys_rt_exec_alarm = ExecAlarm::HardLimit; // Indicate hard limit critical event sys_rt_exec_alarm = ExecAlarm::HardLimit; // Indicate hard limit critical event
} }
static UBaseType_t uxHighWaterMark = 0;
reportTaskStackSize(uxHighWaterMark);
} }
} }

View File

@@ -60,18 +60,14 @@ namespace Motors {
xLastWakeTime = xTaskGetTickCount(); // Initialise the xLastWakeTime variable with the current time. xLastWakeTime = xTaskGetTickCount(); // Initialise the xLastWakeTime variable with the current time.
vTaskDelay(2000); // initial delay vTaskDelay(2000); // initial delay
while (true) { // don't ever return from this or the task dies while (true) { // don't ever return from this or the task dies
// static UBaseType_t uxHighWaterMark = 0;
// if (uxHighWaterMark != uxTaskGetStackHighWaterMark(NULL)) {
// uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
// grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Servo Task Min Stack Space: %d", uxHighWaterMark);
// }
for (Servo* p = List; p; p = p->link) { for (Servo* p = List; p; p = p->link) {
p->update(); p->update();
} }
vTaskDelayUntil(&xLastWakeTime, xUpdate); vTaskDelayUntil(&xLastWakeTime, xUpdate);
static UBaseType_t uxHighWaterMark = 0;
reportTaskStackSize(uxHighWaterMark);
} }
} }

View File

@@ -360,13 +360,10 @@ namespace Motors {
} // sys.state } // sys.state
} // if mask } // if mask
// static UBaseType_t uxHighWaterMark = 0;
// if (uxHighWaterMark != uxTaskGetStackHighWaterMark(NULL)) {
// uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
// grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "SG Task Stack Space: %d", uxHighWaterMark);
// }
vTaskDelayUntil(&xLastWakeTime, xreadSg); vTaskDelayUntil(&xLastWakeTime, xreadSg);
static UBaseType_t uxHighWaterMark = 0;
reportTaskStackSize(uxHighWaterMark);
} }
} }
} }

View File

@@ -938,3 +938,13 @@ char* reportAxisNameMsg(uint8_t axis) {
sprintf(name, "%c Axis", report_get_axis_letter(axis)); sprintf(name, "%c Axis", report_get_axis_letter(axis));
return name; return name;
} }
void reportTaskStackSize(UBaseType_t& saved) {
#ifdef DEBUG_REPORT_STACK_FREE
UBaseType_t newHighWater = uxTaskGetStackHighWaterMark(NULL);
if (newHighWater != saved) {
saved = newHighWater;
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "%s Min Stack Space: %d", pcTaskGetTaskName(NULL), saved);
}
#endif
}

View File

@@ -127,3 +127,5 @@ char report_get_axis_letter(uint8_t axis);
char* reportAxisLimitsMsg(uint8_t axis); char* reportAxisLimitsMsg(uint8_t axis);
char* reportAxisNameMsg(uint8_t axis); char* reportAxisNameMsg(uint8_t axis);
char* reportAxisNameMsg(uint8_t axis, uint8_t dual_axis); char* reportAxisNameMsg(uint8_t axis, uint8_t dual_axis);
void reportTaskStackSize(UBaseType_t& saved);

View File

@@ -81,6 +81,7 @@ boolean closeFile() {
SD_ready_next = false; SD_ready_next = false;
sd_current_line_number = 0; sd_current_line_number = 0;
myFile.close(); myFile.close();
SD.end();
return true; return true;
} }
@@ -147,7 +148,7 @@ uint8_t get_sd_state(bool refresh) {
sd_state = SDCARD_NOT_PRESENT; sd_state = SDCARD_NOT_PRESENT;
//using default value for speed ? should be parameter //using default value for speed ? should be parameter
//refresh content if card was removed //refresh content if card was removed
if (SD.begin((GRBL_SPI_SS == -1) ? SS : GRBL_SPI_SS, SPI, GRBL_SPI_FREQ)) { if (SD.begin((GRBL_SPI_SS == -1) ? SS : GRBL_SPI_SS, SPI, GRBL_SPI_FREQ, "/sd", 2)) {
if (SD.cardSize() > 0) { if (SD.cardSize() > 0) {
sd_state = SDCARD_IDLE; sd_state = SDCARD_IDLE;
} }

View File

@@ -68,7 +68,27 @@ uint8_t serial_get_rx_buffer_available(uint8_t client) {
return client_buffer[client].availableforwrite(); return client_buffer[client].availableforwrite();
} }
void heapCheckTask(void* pvParameters) {
static uint32_t heapSize = 0;
while (true) {
uint32_t newHeapSize = xPortGetFreeHeapSize();
if (newHeapSize != heapSize) {
heapSize = newHeapSize;
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "heap %d", heapSize);
}
vTaskDelay(3000 / portTICK_RATE_MS); // Yield to other tasks
static UBaseType_t uxHighWaterMark = 0;
reportTaskStackSize(uxHighWaterMark);
}
}
void serial_init() { void serial_init() {
#ifdef DEBUG_REPORT_HEAP_SIZE
// For a 2000-word stack, uxTaskGetStackHighWaterMark reports 288 words available
xTaskCreatePinnedToCore(heapCheckTask, "heapTask", 2000, NULL, 1, NULL, 1);
#endif
Serial.begin(BAUD_RATE); Serial.begin(BAUD_RATE);
Serial.setRxBufferSize(256); Serial.setRxBufferSize(256);
// reset all buffers // reset all buffers
@@ -76,9 +96,11 @@ void serial_init() {
grbl_send(CLIENT_SERIAL, "\r\n"); // create some white space after ESP32 boot info grbl_send(CLIENT_SERIAL, "\r\n"); // create some white space after ESP32 boot info
serialCheckTaskHandle = 0; serialCheckTaskHandle = 0;
// create a task to check for incoming data // create a task to check for incoming data
// For a 4096-word stack, uxTaskGetStackHighWaterMark reports 244 words available
// after WebUI attaches.
xTaskCreatePinnedToCore(serialCheckTask, // task xTaskCreatePinnedToCore(serialCheckTask, // task
"serialCheckTask", // name for task "serialCheckTask", // name for task
8192, // size of task stack 4096, // size of task stack
NULL, // parameters NULL, // parameters
1, // priority 1, // priority
&serialCheckTaskHandle, &serialCheckTaskHandle,
@@ -91,6 +113,7 @@ void serial_init() {
void serialCheckTask(void* pvParameters) { void serialCheckTask(void* pvParameters) {
uint8_t data = 0; uint8_t data = 0;
uint8_t client = CLIENT_ALL; // who sent the data uint8_t client = CLIENT_ALL; // who sent the data
static UBaseType_t uxHighWaterMark = 0;
while (true) { // run continuously while (true) { // run continuously
while (any_client_has_data()) { while (any_client_has_data()) {
if (Serial.available()) { if (Serial.available()) {
@@ -149,6 +172,9 @@ void serialCheckTask(void* pvParameters) {
WebUI::Serial2Socket.handle_flush(); WebUI::Serial2Socket.handle_flush();
#endif #endif
vTaskDelay(1 / portTICK_RATE_MS); // Yield to other tasks vTaskDelay(1 / portTICK_RATE_MS); // Yield to other tasks
static UBaseType_t uxHighWaterMark = 0;
reportTaskStackSize(uxHighWaterMark);
} // while(true) } // while(true)
} }

View File

@@ -185,6 +185,9 @@ namespace Spindles {
// Wait a bit before we retry. Set the delay to poll-rate. Not sure // Wait a bit before we retry. Set the delay to poll-rate. Not sure
// if we should use a different value... // if we should use a different value...
vTaskDelay(VFD_RS485_POLL_RATE); vTaskDelay(VFD_RS485_POLL_RATE);
static UBaseType_t uxHighWaterMark = 0;
reportTaskStackSize(uxHighWaterMark);
} }
} }

View File

@@ -123,6 +123,9 @@ void controlCheckTask(void* pvParameters) {
system_exec_control_pin(pins); system_exec_control_pin(pins);
} }
debouncing = false; debouncing = false;
static UBaseType_t uxHighWaterMark = 0;
reportTaskStackSize(uxHighWaterMark);
} }
} }
#endif #endif

View File

@@ -20,8 +20,6 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>. along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "Grbl.h"
// System states. The state variable primarily tracks the individual functions // System states. The state variable primarily tracks the individual functions
// of Grbl to manage each without overlapping. It is also used as a messaging flag for // of Grbl to manage each without overlapping. It is also used as a messaging flag for
// critical events. // critical events.

View File

@@ -600,9 +600,9 @@ namespace WebUI {
#ifdef ENABLE_WIFI #ifdef ENABLE_WIFI
static Error listAPs(char* parameter, AuthenticationLevel auth_level) { // ESP410 static Error listAPs(char* parameter, AuthenticationLevel auth_level) { // ESP410
JSONencoder* j = new JSONencoder(espresponse->client() != CLIENT_WEBUI); JSONencoder j(espresponse->client() != CLIENT_WEBUI);
j->begin(); j.begin();
j->begin_array("AP_LIST"); j.begin_array("AP_LIST");
// An initial async scanNetworks was issued at startup, so there // An initial async scanNetworks was issued at startup, so there
// is a good chance that scan information is already available. // is a good chance that scan information is already available.
int n = WiFi.scanComplete(); int n = WiFi.scanComplete();
@@ -614,12 +614,12 @@ namespace WebUI {
break; break;
default: default:
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
j->begin_object(); j.begin_object();
j->member("SSID", WiFi.SSID(i)); j.member("SSID", WiFi.SSID(i));
j->member("SIGNAL", wifi_config.getSignal(WiFi.RSSI(i))); j.member("SIGNAL", wifi_config.getSignal(WiFi.RSSI(i)));
j->member("IS_PROTECTED", WiFi.encryptionType(i) != WIFI_AUTH_OPEN); j.member("IS_PROTECTED", WiFi.encryptionType(i) != WIFI_AUTH_OPEN);
// j->member("IS_PROTECTED", WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? "0" : "1"); // j->member("IS_PROTECTED", WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? "0" : "1");
j->end_object(); j.end_object();
} }
WiFi.scanDelete(); WiFi.scanDelete();
// Restart the scan in async mode so new data will be available // Restart the scan in async mode so new data will be available
@@ -630,9 +630,8 @@ namespace WebUI {
} }
break; break;
} }
j->end_array(); j.end_array();
webPrint(j->end()); webPrint(j.end());
delete j;
if (espresponse->client() != CLIENT_WEBUI) { if (espresponse->client() != CLIENT_WEBUI) {
espresponse->println(""); espresponse->println("");
} }
@@ -657,17 +656,16 @@ namespace WebUI {
} }
static Error listSettings(char* parameter, AuthenticationLevel auth_level) { // ESP400 static Error listSettings(char* parameter, AuthenticationLevel auth_level) { // ESP400
JSONencoder* j = new JSONencoder(espresponse->client() != CLIENT_WEBUI); JSONencoder j(espresponse->client() != CLIENT_WEBUI);
j->begin(); j.begin();
j->begin_array("EEPROM"); j.begin_array("EEPROM");
for (Setting* js = Setting::List; js; js = js->next()) { for (Setting* js = Setting::List; js; js = js->next()) {
if (js->getType() == WEBSET) { if (js->getType() == WEBSET) {
js->addWebui(j); js->addWebui(&j);
} }
} }
j->end_array(); j.end_array();
webPrint(j->end()); webPrint(j.end());
delete j;
return Error::Ok; return Error::Ok;
} }
@@ -833,15 +831,15 @@ namespace WebUI {
} }
static Error listLocalFilesJSON(char* parameter, AuthenticationLevel auth_level) { // No ESP command static Error listLocalFilesJSON(char* parameter, AuthenticationLevel auth_level) { // No ESP command
JSONencoder* j = new JSONencoder(espresponse->client() != CLIENT_WEBUI); JSONencoder j(espresponse->client() != CLIENT_WEBUI);
j->begin(); j.begin();
j->begin_array("files"); j.begin_array("files");
listDirJSON(SPIFFS, "/", 4, j); listDirJSON(SPIFFS, "/", 4, &j);
j->end_array(); j.end_array();
j->member("total", SPIFFS.totalBytes()); j.member("total", SPIFFS.totalBytes());
j->member("used", SPIFFS.usedBytes()); j.member("used", SPIFFS.usedBytes());
j->member("occupation", String(100 * SPIFFS.usedBytes() / SPIFFS.totalBytes())); j.member("occupation", String(100 * SPIFFS.usedBytes() / SPIFFS.totalBytes()));
webPrint(j->end()); webPrint(j.end());
if (espresponse->client() != CLIENT_WEBUI) { if (espresponse->client() != CLIENT_WEBUI) {
webPrintln(""); webPrintln("");
} }

View File

@@ -28,6 +28,8 @@ build_flags =
-DCORE_DEBUG_LEVEL=0 -DCORE_DEBUG_LEVEL=0
-Wno-unused-variable -Wno-unused-variable
-Wno-unused-function -Wno-unused-function
;-DDEBUG_REPORT_HEAP_SIZE
;-DDEBUG_REPORT_STACK_FREE
[env] [env]
lib_deps = lib_deps =