I am new to Arduino programming and I am working on my first program. I have run into the proverbial "Out of SRAM" problem. Can somebody tell me if a variable goes "out of scope" if it frees up SRAM that can be used by other variables?
Thanks!
Many times Out of SRAM conditions are caused by string constants used for Serial output. You can avoid having those constants take up SRAM space by using the F() macro:
Seeial.print(F("This string constant stays in FLASH memory rather than being copied to SRAM."));
Only some of the variables are lost when they go out of scope. in C/C++ the simple stuff arrays, ints, reals floats and so do disapper
when they go out of scope as they are created on the stack.
However object are created on the heap and need to be expilcitly destroyed before they go out of scope. If you don't when the heap is used up.
Mark
PS there are many other ways to run out of memory why not post your code.
holmes4:
However object are created on the heap and need to be expilcitly destroyed before they go out of scope. If you don't when the heap is used up.
Only if you use new or malloc. While some classes do call those internally (most notably String), most should not.
holmes4:
Can you give me an example of a static object on the Arduino?.
I can have more than 1 serial on the mega so that can't be static. But the pre-processing done by the Arduino's build system makes it hard to tell.
I can have more than one servo or bounce so they can't be and so the list goes on.
You can have more than one int on the stack, and you can have more than one Serial on the stack. You don't need to use new if you want to have more than one int, and you don't need it to have more than one Serial or Bounce or Servo.
learncpp:
In C++, when you use the new operator to allocate memory, this memory is assigned from the heap.
I can't find anything saying that objects go on the stack when they're allocated normally because it's such a silly question.
class myclass {
public:
int i;
};
myclass myobject; // on STACK
void setup() {
myclass myotherobject; // on STACK
} // myotherobject is destroyed
void loop() {
myclass* myclassptr; // pointer is on STACK
myclassptr = new myclass; // object is on HEAP --- must be destroyed
delete myclassptr; // object is no longer on heap
}
This link is also somewhat enlightening:
If you continue to insist that all class objects are created on the heap, you will need to provide references.
(sorry to afuite, this really isn't relevant to your question at all)
holmes4:
You therfore need to free the heap data before the pointer to goes out of scope.
Using an object doesn't imply that it owns any allocated memory. It will only own memory if it allocated it, or it was given it. In either case, the usual rules of memory management would apply and when it was destroyed the object would be responsible for releasing any memory that it owned. I think you're implying that objects inherently or commonly use heap memory and that users of those objects need to deal with that specially, and I don't think that's the case.
On an AVR, registers are part of memory (sort of) and the difference between RAM memory (and registers) and Program Memory is REALLY important (and somewhat difficult to explain.)
Not true a simple type is such as int is on the stack, however an instance of a class eg sevro, sreial etc comes in two parts
the data for that instance of the class eg servo1, servo2 which are created on the heap
pointers to the instance data on the heap. The pointers are created on the stack.
You therfore need to free the heap data before the pointer to goes out of scope.
If not the space in the heap remaines allocated but nothing points to it.
Mark
WRONG. This is only true if you allocate the data that way, by using new. Everything else either has a global lifespan (and is never deleted) or block-level scope, and is automatically deleted when you exit the block. You do not have to manually delete it.
Since you bring up those two classes, here are the class declarations for HardwareSerial and Servo. Please point out to us the "pointers to dynamically allocated memory":
class HardwareSerial : public Stream
{
protected:
volatile uint8_t * const _ubrrh;
volatile uint8_t * const _ubrrl;
volatile uint8_t * const _ucsra;
volatile uint8_t * const _ucsrb;
volatile uint8_t * const _ucsrc;
volatile uint8_t * const _udr;
// Has any byte been written to the UART since begin()
bool _written;
volatile rx_buffer_index_t _rx_buffer_head;
volatile rx_buffer_index_t _rx_buffer_tail;
volatile tx_buffer_index_t _tx_buffer_head;
volatile tx_buffer_index_t _tx_buffer_tail;
// Don't put any members after these buffers, since only the first
// 32 bytes of this struct can be accessed quickly using the ldd
// instruction.
unsigned char _rx_buffer[SERIAL_RX_BUFFER_SIZE];
unsigned char _tx_buffer[SERIAL_TX_BUFFER_SIZE];
public:
inline HardwareSerial(
volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,
volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
volatile uint8_t *ucsrc, volatile uint8_t *udr);
void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
void begin(unsigned long, uint8_t);
void end();
virtual int available(void);
virtual int peek(void);
virtual int read(void);
int availableForWrite(void);
virtual void flush(void);
virtual size_t write(uint8_t);
inline size_t write(unsigned long n) { return write((uint8_t)n); }
inline size_t write(long n) { return write((uint8_t)n); }
inline size_t write(unsigned int n) { return write((uint8_t)n); }
inline size_t write(int n) { return write((uint8_t)n); }
using Print::write; // pull in write(str) and write(buf, size) from Print
operator bool() { return true; }
// Interrupt handlers - Not intended to be called externally
inline void _rx_complete_irq(void);
void _tx_udr_empty_irq(void);
};
class Servo
{
public:
Servo();
uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes.
void detach();
void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds
void writeMicroseconds(int value); // Write pulse width in microseconds
int read(); // returns current pulse width as an angle between 0 and 180 degrees
int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
bool attached(); // return true if this servo is attached, otherwise false
private:
uint8_t servoIndex; // index into the channel data for this servo
int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH
int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
};