Start a new topic

High RAM usage - where to start reducing?

Hi All. I'm a new user of the Nextion 2.4" display, running it on an Arduino Micro.  It's all working just fine. I have run it from the RX/TX pins (0/1) and also from pins 10/11 (by changing the NexConfig.h definition of nexSerial).  In short, all working as expected - at least as much as I've tried.


The problem is, I'm working in an extremely tight memory environment - I am running a MicroSD card from which I am streaming WAV files which encode video and audio for a mechanical television.  The Nextion is controlling the user interface for the TV - brightness, contrast, volume, etc.


A bit of a plug:  here's my project video playlist

https://www.youtube.com/playlist?list=PLBo1eD9x0FwsKHMemeg55uVL2CDSLgNPr


So anyway, on to my question - because I'm doing an awful lot with the little Arduino, memory is very tight - and I have just found out that a MAJOR hog of memory is the Nextion code.  If I have a minimal stand-alone Nextion test program which simply sets up a few sliders and callbacks, the RAM usage shoots to 48% (from 5% for an empty project).  That's basically 43% of all available RAM used just by the Nextion code. And compared to an empty project, the Nextion library is using 30% of the available ROM, too!!! This is really really high and costly.


Oh, the question - what is using all the memory, and where/how best to tackle reducing it?  I thought I'd ask here before starting a hunt trough the code base.


Thanks for any assistance. Hope I havent' broken any forum rules. For the record, I love the hardware - amazing for the price!


 

// Minimal callback test of Nextion UI for Mechanical TV on Arduino Micro
// by Andrew Davie (andrew@taswegian.com)

#include "NexSlider.h"
#include "NexText.h"
#include "NexButton.h"
#include "NexCheckbox.h"
#include "NexText.h"

NexSlider seekerSlider = NexSlider(0, 12, "seek");
NexSlider brightnessSlider = NexSlider(0, 8, "brightness");
NexSlider contrastSlider = NexSlider(0, 10, "contrast");
NexSlider volumeSlider = NexSlider(0, 11, "volume");
NexCheckbox gammaCheckbox = NexCheckbox(0, 3, "gamma");
NexButton stopButton = NexButton(0, 4, "stopButton");
NexText timePos = NexText(0, 9, "timePos");

NexTouch *nex_Listen_List[] = {
    &stopButton,
    &seekerSlider,
    &contrastSlider,
    &brightnessSlider,
    &gammaCheckbox,
    &volumeSlider,
    NULL
};

boolean stopped = false;

void stopButtonCallback(void *ptr) {
  NexButton *stopButton = (NexButton *) ptr;
  stopped = !stopped;
  Serial.print(F("STOP = "));
  Serial.println(stopped);
}

void brightnessCallback(void *ptr) {
  NexSlider *slider = (NexSlider *) ptr;
  long bright;
  slider->getValue(&bright);
  Serial.print(F("BRIGHTNESS = "));
  Serial.println(bright);
}

void contrastCallback(void *ptr) {
  NexSlider *slider = (NexSlider *) ptr;
  long contrast;
  slider->getValue(&contrast);
  Serial.print(F("CONTRAST= "));
  Serial.println(contrast);
}

void volumeCallback(void *ptr) {
  NexSlider *slider = (NexSlider *) ptr;
  long volume;
  slider->getValue(&volume);
  Serial.print(F("VOLUME ="));
  Serial.println(volume);
}

void seekerCallback(void *ptr) {
  NexSlider *slider = (NexSlider *) ptr;
  long seekPosition;
  slider->getValue(&seekPosition);
  Serial.print(F("SEEK ="));
  Serial.println(seekPosition);
}


void gammaCallback(void *ptr) {
  NexCheckbox *checkbox = (NexCheckbox *) ptr;
  long gamma;
  checkbox->getValue(&gamma);
  Serial.print(F("GAMMA ="));
  Serial.println(gamma);
}

void setup(void) {
    nexInit();
    seekerSlider.attachPop(seekerCallback, &seekerSlider);
    brightnessSlider.attachPop(brightnessCallback, &brightnessSlider);
    contrastSlider.attachPop(contrastCallback, &contrastSlider);
    volumeSlider.attachPop(volumeCallback, &volumeSlider);
    gammaCheckbox.attachPop(gammaCallback, &gammaCheckbox);
    stopButton.attachPop(stopButtonCallback, &stopButton);
}
void loop(void) {
    nexLoop(nex_Listen_List);
}

 


Hi Andrew


What are the specs for your Micro

https://www.arduino.cc/en/Main/arduinoBoardMicro

32KiB of flash memory, 2.5KiB of SRAM.


The testbed posted in the original post uses over 1K of that precious RAM. I was asking what's using all that memory?

First compare to a near empty sketch


#include <Arduino.h>


void setup ( void) {

  Serial.begin(9600);

  Serial1.begin(115200);

}


void loop() {

  int i;

  i++;

}


The first chunk of memory gone is for Arduino

 - this is including Serial buffers needed for RX/TX

Then with this type of baseline, you can see difference between this and Nextion.

Also good to note that when selecting a limited sram (2560 byte) microprocessor

is that sram/fram is available as an add-on as is spi flash.


The reason to pick a 2.5K microcontroller is when it fits your needs

If small is the appeal, an STM32F103C8T6 is 3 pins longer at 20K

or an ESP8266-12 has nearly 50K sram.


But to add 64K or 128K sram or 32K fram MCU side is not out of question

I want to make a point here


When I compile your "minimal" code provided above for micro

Arduino reports 8606 bytes (30%) of program storage space

Global variables use 631 bytes (24%) of dynamic memory


When I compile basic with two serials open for micro I get

Sketch uses 4414 bytes (15%) of program storage space

Global variables use 321 bytes (12%) of dynamic memory


At this point the difference is what Nextion consumes

  4192 bytes storage, 310 bytes sram (12.1% of 2560)


So what is off here?  Your claim was 43% of used by Nextion


Interesting. Thank you for checking.

I will re-test because I don't exactly understand your numbers or what you did.

When I compiled my minimal example, I got the quoted number (43% of RAM used).

I will come back with a confirmation tomorrow.


You may have a point about the cost of having a serial port; me bad, I didn't consider that.

As I said, I will do some more tests.  310 bytes is very reasonable.


Sounds like my claim is off - I will re-do the tests, as I said.

Thanks again for the reply.


Arduino IDE 1.80 - Board Arduino/Genuino Micro

First test was basic as I suggested to do above.


#include <Arduino.h>


void setup ( void) {

  Serial.begin(9600);

  Serial1.begin(115200);

}

void loop() {

  int i;

  i++;

}


Second test was your code as posted above.

I think I have possibly found the problem and it is probably my fault, sorry. I am using a software serial and have defined this in the NexConfig.h and obviously that is using all the RAM. I was/am having issues downloading when the Nextion is connected to RX/TX so switched it and seemed to be working fine. I had forgotten this reconfiguration and now see that I have modified the file thus...


/** 
 * Define DEBUG_SERIAL_ENABLE to enable debug serial. 
 * Comment it to disable debug serial. 
 */
//#define DEBUG_SERIAL_ENABLE

/**
 * Define dbSerial for the output of debug messages. 
 */
#define dbSerial Serial

/**
 * Define nexSerial for communicate with Nextion touch panel. 
 */

#ifdef NEXTION_SERIAL_REDEFINE
	#include <SoftwareSerial.h>
	extern SoftwareSerial NexSerial;
	#define nexSerial NexSerial
#else
	#define nexSerial Serial1
#endif

 

Clearly this is a user error - I will have to find a way to go back to hardware serial.

I apologise for the error, and thank you for your support.


To go back to Hardware Serial



 

/**
 * @file NexConfig.h
 *
 * Options for user can be found here. 
 *
 * @author  Wu Pengfei (email:<pengfei.wu@itead.cc>)
 * @date    2015/8/13
 * @copyright 
 * Copyright (C) 2014-2015 ITEAD Intelligent Systems Co., Ltd. \n
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 */
#ifndef __NEXCONFIG_H__
#define __NEXCONFIG_H__

/**
 * @addtogroup Configuration 
 * @{ 
 */

/** 
 * Define DEBUG_SERIAL_ENABLE to enable debug serial. 
 * Comment it to disable debug serial. 
 */
#define DEBUG_SERIAL_ENABLE

/**
 * Define dbSerial for the output of debug messages. 
 */
#define dbSerial Serial

/**
 * Define nexSerial for communicate with Nextion touch panel. 
 */
#define nexSerial Serial1

#ifdef DEBUG_SERIAL_ENABLE
#define dbSerialPrint(a)    dbSerial.print(a)
#define dbSerialPrintln(a)  dbSerial.println(a)
#define dbSerialBegin(a)    dbSerial.begin(a)
#else
#define dbSerialPrint(a)    do{}while(0)
#define dbSerialPrintln(a)  do{}while(0)
#define dbSerialBegin(a)    do{}while(0)
#endif

/**
 * @}
 */

#endif /* #ifndef __NEXCONFIG_H__ *

 

Serial is Debug in Serial Monitor

Serial1 is connected to Nextion

Unfortunately, despite my suspicions I had caused this problem, I don't seem to have found the issue. I am now testing on a completely different machine and have started from scratch. I am also using a completely different Arduino (Nano).


1) Install Arduino IDE

2) Download ITEAD Nextion library from GitHub and place in ~/Documents/Arduino/libraries

3) Create a new completely blank sketch, and compile. 

Sketch uses 444 bytes (1%) of program storage space. Maximum is 30720 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes. 

4) Copy the following code over the top of the minimal sketch code...


// Minimal callback test of Nextion UI for Mechanical TV on Arduino Micro

#include "NexSlider.h"

NexSlider seekerSlider = NexSlider(0, 12, "seek");

NexTouch *nex_Listen_List[] = {
    &seekerSlider,
    NULL
};

void seekerCallback(void *ptr) {
  NexSlider *slider = (NexSlider *) ptr;
  uint32_t seekPosition;
  slider->getValue(&seekPosition);
}

void setup(void) {
    nexInit();
    seekerSlider.attachPop(seekerCallback, &seekerSlider);
}
void loop(void) {
    nexLoop(nex_Listen_List);
}

 Compile, giving lots of errors related to Serial2...

In file included from /Users/dav425/Documents/Arduino/libraries/ITEADLIB_Arduino_Nextion-master/NexHardware.h:18:0,
                 from /Users/dav425/Documents/Arduino/libraries/ITEADLIB_Arduino_Nextion-master/NexHardware.cpp:15:
/Users/dav425/Documents/Arduino/libraries/ITEADLIB_Arduino_Nextion-master/NexHardware.cpp: In function 'bool recvRetNumber(uint32_t*, uint32_t)':
/Users/dav425/Documents/Arduino/libraries/ITEADLIB_Arduino_Nextion-master/NexConfig.h:37:19: error: 'Serial2' was not declared in this scope
 #define nexSerial Serial2

 So, fix that by editing NexConfig.h...

 

/** 
 * Define DEBUG_SERIAL_ENABLE to enable debug serial. 
 * Comment it to disable debug serial. 
 */
//#define DEBUG_SERIAL_ENABLE

/**
 * Define dbSerial for the output of debug messages. 
 */
#define dbSerial Serial

/**
 * Define nexSerial for communicate with Nextion touch panel. 
 */
#define nexSerial Serial

 In short, disabled DEBUG_SERIAL_ENABLE and changed Serial2 to Serial (I just want to do a compile comparision, I don't care if the Nextion actually works)...


Now compile:

 

Sketch uses 7260 bytes (23%) of program storage space. Maximum is 30720 bytes.
Global variables use 857 bytes (41%) of dynamic memory, leaving 1191 bytes for local variables. Maximum is 2048 bytes.

 

Analysis:


My barebones example with just one Nextion variable (a slider) and simplest possible callback is using 848 bytes of RAM more than the blank sketch.  In this case, over 40% of available RAM.


Here I have used a new machine, a different arduino, and a completely fresh install of Arduino IDE and the ITEAD library. And yet I'm getting large RAM usage. I just can't get it to near the 300 bytes that you see.


What am I doing wrong?

Is the change from Serial2 to Serial the culprit?  What should I be using in its place?


Thanks

A


Again for consistency - Serial needs to be in initial comparison

#include <Arduino.h>

void setup ( void) { 
  Serial.begin(9600);
  Serial1.begin(115200);
}
void loop() { 
  int i;
  i++;
}

Next, the library is not necessarily needed - depends on programming skills.

So if you feel comfortable programming your own serial routines, perhaps you can whittle it down


But you are making huge error in NexConfig.h

Refer to Arduino Reference for #define

Debug Serial and port for Nextion are not the same.

Replacements for dbSerial to Serial could be problematic for sure when nexSerial is also Serial


Refer to Iteadlib readme.md - follow UNO adjustments when only one serial for how to do


Thanks for the reply. I might program my own, as there is a lot that I do not need in the library.  Without wasting too much time, I copied your above example, but without Serial1 because it is undefined with the code given.  The result: 

Sketch uses 1390 bytes (4%) of program storage space. Maximum is 30720 bytes.
Global variables use 182 bytes (8%) of dynamic memory, leaving 1866 bytes for local variables. Maximum is 2048 bytes.

That still leaves the Nextion library (now exactly the same, both using only Serial) being (857-182=675) bytes larger.  Lower, now rougly 1/3 of RAM for this Arduino. Thanks for pointing out I should be comparing fairly.


I realise the "Huge error" to which you refer.  I was simply intending to get the code to compile to see how many bytes used - I specifically stated it would not work for this reason "(I just want to do a compile comparison, I don't care if the Nextion actually works)..." - but thanks for pointing it out.

 

I had not read it, but believe I did exactly what the documentation suggests for UNO-like adjustment for a single serial port.


Probably the best way for me to go is implement a minimal functional subset and do the comms myself - am capable enough.


Thank you for your responsive and helpful support.


You do understand that compilers for embedded usually

 - only include the machine code needed for what is called

 - function not used is not included

Once function is included for first instance,

   second instance isn't double, but only fractional


byte - 1 byte, word aligned 2-4 bytes

int  - 2 to 4 bytes dependent on MCU

long - 4 bytes

string - 1 byte per char + 1 for null termination


String handling routines ... much more complex than numbers


But I also noted specs Nano vs Micro - Nano lost 1/2K sram

I have been a professional programmer for nearly 40 years and have the highest formal qualifications - a Doctorate in Computing. I kind of know what I'm doing. Sure I make mistakes - we all do. I have programmed many, many embedded systems. I understand how a linker works, and how only referenced functions are included. I don't quite understand what you are trying to tell me, though. It is clear to me that 675 bytes is the Nextion RAM footprint. I can't see any error and I've given a minimum example and steps to reproduce my results - I can't see how you can manage to get it down to ~300 bytes which is why I was asking "what am I doing wrong"!  But it's not important - I'll spin my own code instead.

Thanks for the reply.

Login or Signup to post a comment