Declaring a file-scope object in a function in a library

Hey all,

I'm working on a relatively simple library for one of the HRXL MaxBotix rangefinders. It has three main ways of sending the range data - analog, pulse width and serial. At a high level, I've set up the library such that you declare a sonar object, and then set the mode of that object so that the signals are processed correctly.

The goal: In the sonar::setMode(...) function, I want to create a SoftwareSerial object that can be seen by the sonar::getRange(uint16_t *range) function.

I do not want to declare the SoftwareSerial object globally because the object is large (hundreds of bytes of RAM), and is only rarely needed (e.g. if the sonar is only used in analog mode, it's a needless waste of RAM). Even if I do declare it globally with dummy values (since we don't have the pin number or logic_inverted flag yet), there is no way to change those values to the correct values once the constructor has been called (because of the way the SoftwareSerial library was written).

Here's the important slice of code from the library illustrating the issue:

uint8_t sonarHRXL::setMode(unsigned int mode, uint8_t signalPin){
	// If the sonar was previously configured, disable previous mode
	if(_configured && (_mode == TTL || _mode == RS232))
		// Call the destructor
		sonar.end(); // error - sonar has not been declared here yet - how to fix without global declaration?

	// Configure the sonar in the new mode
	if(mode == TTL || mode == RS232){
		// the signal pin is RX, TX pin is an invalid placeholder, invert logic if RS232 mode is used
		static SoftwareSerial sonar(signalPin, 0xFF, mode == RS232);

		sonar.begin(9600);
	} else if (mode == PWM || mode == ANALOG)
		pinMode(signalPin, INPUT);
	else
		return INVALIDINPUT;

	_signalPin = signalPin;
	_mode = mode;
	_configured = true;
	return SUCCESS;
}

static uint8_t sonarHRXL::getRange(uint16_t *range){
	// Check that data is available
	if(sonar.available() >= 5) { // error: 'sonar' was not declared in this scope
	//...

Does anyone know how to accomplish this? I need to declare and construct an object within a function that persists and can be reached by other functions

The only thing I've come up with is this awful solution, which is to declare it globally (often wasting lots of RAM), then destroying the object and reconstructing it. However, I couldn't even get that to work - I get "error: no matching function for call to 'operator new(sizetype, SoftwareSerial*)'"

Thank you in advance for any help you can offer!

sandiegoguy:
Does anyone know how to accomplish this? I need to declare and construct an object within a function that persists and can be reached by other functions

What do you mean by other functions? Other member functions of the same class?
If so, maybe could you have a private variable (static or instance?, I'm not sure what you need):

SoftwareSerial *mySerial;

When you need to use SoftwareSerial:

mySerial = new SoftwareSerial(signalPin, 0xFF, mode == RS232);

When you're done with it:

delete mySerial;

The compile gives some warnings on the 'delete' operation, so you'll need to make sure the SoftwareSerial destructor properly cleans things up.

Ah, perfect, that was it, thanks! In classic shower-thoughts style, I figured it should have just been a pointer so I could call the constructor later, but still wasn't sure of the details. Got it running nicely now.

To help anyone in the future with a similar issue, here's the code I ended up going with:

#if _NEEDSERIAL 
	ReceiveOnlySoftwareSerial* sonar;
#endif

//...

uint8_t sonarHRXL::setMode(unsigned int mode, uint8_t signalPin){
	// if previously configured, disable previous mode
	#if _NEEDSERIAL
		if(_configured && (_mode == TTL || _mode == RS232))
			sonar->end(); // calls destructor
	#endif 

	// configure new mode
	if(mode == TTL || mode == RS232){
		#if _NEEDSERIAL
			sonar = new SoftwareSerial(signalPin, 0xFF, mode == RS232);
			sonar->begin(9600);
		#else
			return SERIALDISABLED;
		#endif
	} else if (mode == PWM || mode == ANALOG)
		pinMode(signalPin, INPUT);
	else
		return INVALIDINPUT;

	_signalPin = signalPin;
	_mode = mode;
	_configured = true;
	return SUCCESS;
}

Remember that you have to use the -> operator to call member functions from the pointer.

Unfortunately, with this setup, the compiler doesn't quite optimize away much of the SoftwareSerial library if it's not used. I ended up using preprocessor commands to remove the library if it's not needed, but that's not ideal.

FLASH RAM
With serial removed via flag 2948 bytes (9%) 289 bytes (14%)
With serial enabled via flag and not used 5726 bytes (17%) 329 bytes (16%)
With serial enabled via flag and used 6868 bytes (21%) 389 bytes (18%)
With serial enabled via flag and implicitly used
(iterates through modes)
6950 bytes (21%) 391 bytes (19%)

If there something I'm missing for why the preprocessor is still linking the library if it's not needed? Certain things need to be declared static?

Thanks again for the help!

Because the Arduino environment doesn't perfectly fit compilers originally designed for UNIX/desktop use. The SoftwareSerial library uses interrupts and the optimiser cannot tell if those interrupts are used or not. So it must compile them into the final object code.

Ah, that clears it up succinctly - thanks