Dynamic Serial port selection and use without instantiating all Serialx objects

I have an arduino project that reads a config file from SD and then receives data on a serial port that is specifed in the config file.

I therefore need to instantiate the Serial (Serial0), Serial1,..2,3, etc based on dynamic data.

I am using a mega2560 with 4x HardwareSerial. Serial (also Serial0) is the USB. Serial 1, 2, 3 are on specific hardware pins.

I started with code something like:

uint8_t InitPort(uint8_t port) {
    switch (port) {
        case 0:
            if(Serial.begin(9600) return 1;
            break;
        case 1:
            if(Serial1.begin(9600) return 1;
            break;

        ... etc...

        default:
            return 0;
}

However, this instantiates all Serialx objects (using 157 bytes of data/bss each).

I've since written a class wrapper that takes a referece to the desired serial object as an initialising parameter, but this just pushes the problem up-stack to the calling code.

Is there a way that a Serial object can be instanitated dynamically with a port number passed in, to only end up with one instance connected to the desired port?

winginit:
However, this instantiates all Serialx objects (using 157 bytes of data/bss each).

“Dynamic” and “instantiates all” are mutually inclusive.

Your objection to “instantiates all” and your requirement for “dynamic” indicates you have excluded pertinent details.

Where, in your code sample, do you think there is ANY object instantiation occurring? You are calling the begin() method of an already existing Serial object....

Regards,
Ray L.

Just on another note

The begin() method doesn't return anything so using it in a condition is useless and can possibly give incorrect results.

From HardwareSerial.h

    void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
    void begin(unsigned long, uint8_t);

OK, with some deeper undertsanding now and in resposne to your posts (many thanks):

I see now that the Serial objects are global variables. If they are not used in the application, the linker optimises them out so they do not use memory. However, if it is not known until run-time which ones will be used (becuase that is specified when the application reads its configuration file from SD), and beccase they are referred to in the code the linker necessarily incudes all 4x.

I posted this question on arduino StackExchange too, and it now has an accepted answer that seems to solve the problem by instantiating a new HardwareSerial dynamically using the relevant registers? as initialisers. It does however require some ISR gymnastics as fully explained in the answer.

Question and answer can be found here.

Regarding the .begin(), many thanks I will look further into that. Interestingly I have two identical boards, one dev the other in production. The production board has a Victron battery monitor connected to Serial3, the dev one has nothing connected to Serial3. The production system returns true on a call to begin(), the dev fails false. So on that basis I have been using .begin() as a connection test, though I freely admit to not having a proper comprehension of why this works. Is there a best practice way of testing "connectedness" in this regard?

Many thanks
Brendan

sterretje:
Just on another note

The begin() method doesn’t return anything so using it in a condition is useless and can possibly give incorrect results.

From HardwareSerial.h

    void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }

void begin(unsigned long, uint8_t);

Apologies, you are quite right. My example code derives from the class wrapper I wrote in which I have a method ::begin() and I got confused in writing the question.

In my wrapper I call Serial3.begin(); and then test:

if( ) {

}

This is the current basis for my “connected” test though I haven’t yet picked apart the initaliser to see why it works. Given that its global variable pointing to a class instance I assume that it will have a valid address (ie not NULL)… some more digging required.

Regards,
Brendan

winginit:
However, this instantiates all Serialx objects (using 157 bytes of data/bss each).

winginit:
I posted this question on arduino StackExchange too, and it now has an accepted answer that seems to solve the problem by instantiating a new HardwareSerial dynamically using the relevant registers? as initialisers. It does however require some ISR gymnastics as fully explained in the answer.

Question and answer can be found here.

Beware of false economies. Are you solving a problem that you don't have?

gfvalvo:
Beware of false economies. Are you solving a problem that you don't have?

Good advice! :slight_smile:

Long answer to a short statement: (not sure if its appropriate for here.... but anyways...)

I hadn't written a line of code for 25 years. We bought a fodder growing box for my farm:

  • Beef cattle
  • Box is roughly a 20 foot shipping container
  • Load barley seed on a flight of trays one end and harvest a sprouted flight of fodder biscuits from the other end
  • End to end about 6 days. It produces 250kg of sprouted barley every 1.5 days on average
  • All off-grid and with no telco / mobile coverage.

Had some time away from work and family seculuded at my farm; observed that the box we bought was hopeless on environment control - was just a watering timer and air con; stole my boys school arduino boards; had a play; thought I could do better with the box; too much time; invented an elaborate IoT control system architecture; printed out my original Berkely ANSI C and C pointers compsci papers for revision; went to work.

In hindsight, it was an overly ambitious project to run on an arduino. Its now some 16,000 lines of code, 900mhz radio links, satelite data feed, rpi / kivy controller, sensors and acutators everywhere, cloud data logging, grammar meta language to define protocols producing memory optimised parsers for management of terminal and configuration interactions, baisc term emulation and command line parsing for interactive control based on the generated parser / grammar ....

The heart of it still runs on an arduino mega and is an algorythmic AI. Its doing commercial work and feeding cows. And doing a good job. Furthermore its got investor interest for various applications here in Australia (with China knocking on the door),

At various stages I've spend countless hours optimising the code to give me enough working memory for the thing to operate larger rule sets. Currently down to 356 bytes of .data. Still work to do on the parser / terminal module consuming the best share of 1900 .bss and with too much calling stack. (and some malloc / frees to weed out).

So my drive for memory optimisation is a short term jail cell based on its evolution to date. (and is also a fun programming goal).

Its way short of being consumable as a product at this stage - still locked in my private brain cycle between dev and feeding cows, though you're welcome to browse here if you agree to be kind :slight_smile:

No reason you can't create whatever Serial object you need, only when you need it. Basically this should work, with proper arguments to the constructor:

HardwareSerial *mySerial = new HardwareSerial(buncha arguments here to define registers for this UART);
mySerial->begin(115200);

...

mySerial->print("Hello\n");

Regards,
Ray L.

winginit:
If they are not used in the application, the linker optimises them out so they do not use memory.

Partially. Everything associated with the interrupt service routines has to be kept.

Those must be some smart cows if the feeder is 16000 LOC.

The MEGA has more memory than the UNO just to store more stuff for more serial ports. The Teensy 3.5 has even more memory and 5 hardware serial.

But a Teensy 3.5, while 5V-tolerant, can only output 3.3V which is likely to require major redesign of what you have.

is not. read here Dynamic Serial port selection and use without instantiating all Serialx objects - Arduino Stack Exchange

MorganS:
Those must be some smart cows if the feeder is 16000 LOC.

lol Smart food. Still dumb cows!

Its acutally up to 24000 lines! Though the scope is quite a bit more elaborate than just fodder growing. I'm using the fodder factory as by dev benchmark, but fully intdend to strip back the full monty version for sub components that can use minimal boards for sub-systems that then communicate back to a smarter hub. I'd orignially hoped that could be a mega, but increasingly I'm moving executive functions and message handling to a Rasperry Pie runing combinations of linux tools and python.