How divide a sketch

Hi, i wanted to know if it´s possible divide a sketch. I mean I want to make a sketch with main program and then create other sketch with all the functions i use in the main sketch, is that possible? is only to avoid use a very long main sketch. Thanks for all =D

Look into #include directive. It is not just for libraries.

You can also use Tabs within the IDE, the button for them is over on the right hand side.
For larger programs, I make a few:
main tab is the file name, I put some note there.
then a_presetup, all the # stuff, pin assigments, variable declarations
b_setup, this is void setup
c_void, the main sketch, maybe broken up also
d_more_void for example
e_functions
f_more_functions

sometimes I'll have a tab with just
void loop()
{

and then later

}// end void loop

so that CTRL-T, autoformat, will work correctly on the intermediate tabs and not choke on a missing { or }

CrossRoads:
You can also use Tabs within the IDE, the button for them is over on the right hand side.
For larger programs, I make a few:
main tab is the file name, I put some note there.
then a_presetup, all the # stuff, pin assigments, variable declarations
b_setup, this is void setup
c_void, the main sketch, maybe broken up also
d_more_void for example
e_functions
f_more_functions

This sounds alarmingly as if you are splitting function implementations across files, which is a very poor idea IMO. If a function is getting unwieldy, it should be refactored into multiple functions (which may be stored in the same file, or separate files).

@CrossRoads:
The multiple program file extensions are "*.ino" ?

@PeterH:
Am I remembering correctly that Arduino GUI does not prototype functions for all but the main INO ? I cannot remember if this was a pre-1.0 issue or if it is even still an issue.

Thx

Ray

Sounds only. In actual implementation it has worked out quite well.
The tabs are broken up into complete sections that can stand alone.
I think they all appear in the directory as .ino. I open the main file, the tabs are there for editing.
I think if you try to open one of the tabs that the main file and all tabs open anyway.

Largest was 7 tabs that compile to 13000+ bytes and works well.
Naming them alphabetically seems to keep the program flow I wanted.
I don't actually have any functions outside of setup & loop (they are technically functions, yes?), it's more state machine like with a check for a flag to indicate if the code in a tab is to run or not:
100mS elapsed? update time counters, set the display update flag.
Serial message recieved? update stuff as needed, set the display update flag.
RF message received? update stuff as needed, set the display update flag.
Update flag set? Clear it and update the display with data set elsewhere.

So most of the time it's 4 checks to see that nothing has happened, resulting in a very responsive system when something does happen.

In my programming of Arduino since summer-ish 2010, I have not written any function outside of setup & loop, only 2 ISRs for interrupts (one to wake from sleep for a remote, and one to deal with PCINTs from 3 rotary encoders). Everything else has been setting flags and then acting on them, which results in function-like operation I suppose without all the time of popping stuff on & off the stack and all that goes on with a function call.
Works for me.

In actual implementation it has worked out quite well.

I have also used the technique a few times when writing long code. I never had an issue the few times, but the function prototyping "thing" came to mind because I answered a post a day or two ago in that regard. I have never tried to "break" the automatic prototyping... I guess this is one of those Ummmm issues.

Ray

The way I understand it, prototyping for functions is the same as declaring variable types (byte, int, etc), and the IDE is pretty good at doing that automatically, but if you do it explicitly then you get what you want.
Now, since I have never actually written a function (outside of a couple of ISRs and used a couple volatile variables ), I have never run into any issues with that.
I also globally declare all my variables so I don't run into scope issues, and any one piece of the sketch can use data from any other part of the sketch.
As I've said before, it works for me in this embedded world.

I also globally declare all my variables so I don't run into scope issues, and any one piece of the sketch can use data from any other part of the sketch.

And this is what I was attempting to convey to the new member CactusFace in a separate but related post, but I come from languages with strong scoping rules and I generally go lightly on global vars, not consciously, but just a habit.

So, back to the original question....

Can you break-up your sketch into multiple modules? Yes, you can by making new tabs in the Arduini GUI and giving the tabs filenames ending in the ".ino" extension. Keep void loop() and void setup() in the default automatically created) module.

If your functions are simple, there should be no problems. But, read this: When are function prototypes necessary? - Syntax & Programs - Arduino Forum
Your mileage may vary!

Ray

You don't have to give the tabs .ino names,the IDE does that by itself when you save the file.
This is what a program might look like, and what results are these files.

It's your sketch and whatever works for you at the end of the day, but even on the small programs suitable to be run on an Arduino it is IMO worth writing structured code and that would normally eliminate the need for very long functions. I'd prefer functions to be small enough to see the whole thing on the screen, and IMO if it's too long to be stored in a single file it's certainly too long to be a single function. If you're offended by the thought of a extra function call you can always declare them as inline functions, but I would have thought the overhead of a function call was irrelevant in all but the most performance-critical applications.

Here's an example of what I would put in a tab. By your explanation this would be broken up into numerous somethings.
I'd rather see one tab that performs a task, maybe split in two if it was too tedious to scroll thru, and the 2nd would just be a continuation of the first. What I really despise is trying to read some code that's been functionalized to death and it becomes a struggle just to find the functions just to see what is going on.
Software engineers seem to enjoy that tho. Not my cup of tea (not that I drink tea, unless it has lemon & sugar to cover up the tea taste 8)

//d_serial_receive

// see if data came in, add to end of the array, or adjust movement
if (Serial.available()>0){
  incomingByte = Serial.read();
  goodFont = 3;

  // mapping from incoming character to font_array or other commands: \ + = < >
  // invalid characters are not defined and are ignored
  switch(incomingByte){
  case 0x20:
    fontArrayPointer = 0; // space
    goodFont = 1; // good character to add to displayArray
    break;
  case 0x21:
    fontArrayPointer = 5; // !
    goodFont = 1; // good character to add to displayArray
    break;
  case 0x27:
    fontArrayPointer = 10; // '
    goodFont = 1; // good character to add to displayArray
    break;
  case 0x2c:
    fontArrayPointer = 15; // ,
    goodFont = 1; // good character to add to displayArray
    break;
  case 0x23:
    fontArrayPointer = 20; // #
    goodFont = 1; // good character to add to displayArray
    break;
  case 0x28:
    fontArrayPointer = 25; //  dash -
    goodFont = 1; // good character to add to displayArray
    break;
  case 0x29:
    fontArrayPointer = 30; //  dash )
    goodFont = 1; // good character to add to displayArray
    break;
  case 0x2d:
    fontArrayPointer = 35; //  dash (
    goodFont = 1; // good character to add to displayArray
    break;
  case 0x2e:
    fontArrayPointer = 40; // period .
    goodFont = 1; // good character to add to displayArray
    break;
  case 0x2f:
    fontArrayPointer = 45; // slash /
    goodFont = 1; // good character to add to displayArray
    break;
  case 0x30:
    fontArrayPointer = 50; // 0
    goodFont = 1; // good character to add to displayArray
    break;
:
bunch deleted to fit the post limits
:
  case 0x5c:
    // use 0x5c '\' for backspace to delete characters
    move_control = backspace;
    goodFont = 2; // not a character to add to displayArray
    break;
  case 0x50:
    // capital P for switch to PC data entry
    // need 3 in a row
    PCmode = PCmode +1;
    if (PCmode == 3){
      //dataEntry = PC;
      PCmode = 0;
      //Serial.println ("Clearing EEPROM length ");
      EEPROM.write(0,0);
      EEPROM.write(1,0);
      //Serial.println("Resetting display ");
      displayStart = 1;
      displayEnd = 8;
      for (x = 0;x<1024;x=x+1){
        displayArray[x] = 0;
      }
    }
    break;
  case 0x53:
    // capital S for saving to EEPROM
    SaveMode = SaveMode + 1;
    if (SaveMode == 3){
      //dataEntry = Memory;
      SaveMode = 0;
      // save length in EEPROM, write displayArray to EEPROM
      Serial.print ("saving to EEPROM, displayEnd = ");
      EEPROM.write(0, (lowByte (displayEnd)));
      EEPROM.write(1, (highByte (displayEnd)));
      Serial.print (EEPROM.read(1));
      Serial.print (" ");
      Serial.println (EEPROM.read(0));
      for (x = 2; x<(arraySize); x=x+1){
        EEPROM.write(x, displayArray[x]);
        Serial.print(x);
        Serial.print (" ");
        Serial.println (EEPROM.read(x));
      }

      Serial.print ("EEPROM address 0 = ");
      Serial.println (EEPROM.read(0));
      Serial.print ("EEPROM address 1 = ");
      Serial.println (EEPROM.read(1));

      highEEPROM = EEPROM.read(1);
      lowEEPROM = EEPROM.read(0);
      EEPROMend = (highEEPROM<<8) + lowEEPROM;
      Serial.print ("EEPROMend = ");
      Serial.println (EEPROMend);

    }
    break;
  } // end switch

  // is there room to add characters? 
  // array defined as arraySize/6 characters long
  // 
  if ((displayEnd < (arraySize - 6)) && (goodFont ==1)){
    goodFont = 0; // reset for next pass
    // add in 6 bytes, 5 from fontArray in PROGMEM & 1 spacer column

    displayArray[displayEnd] = pgm_read_byte(&fontArray[fontArrayPointer]); // font column0
    displayArray[displayEnd+1] = pgm_read_byte(&fontArray[fontArrayPointer+1]); // font column1
    displayArray[displayEnd+2] = pgm_read_byte(&fontArray[fontArrayPointer+2]); // font column2
    displayArray[displayEnd+3] = pgm_read_byte(&fontArray[fontArrayPointer+3]); // font column3
    displayArray[displayEnd+4] = pgm_read_byte(&fontArray[fontArrayPointer+4]); // font column4
    displayArray[displayEnd+5] = 0x00; // spacer/blank column
    displayEnd = displayEnd + 6; // move end out

    updateDisplay = 1;  // send to MAX7219s
  } // end add character
}// end serial available

CrossRoads:
By your explanation this would be broken up into numerous somethings.

Yes, absolutely - and a quick skim through the code suggests that would not be hard to do and would IMO make the logic much clearer.

I have ~12 classes at this point and will be adding ~20 more before the project is done. I'm a software developer who is getting into electronics so I don't need too much help with the code aspect but I do need some help in managing the ide with large number of tabs / classes because it is wasting a lot of time currently with how long it takes to view one class and then another. Any ideas?

Apparently Atom editor with PlatformIO plugin is good?

Hey ryanb9, notice this is a 5 year old thread, so in all likelyhood, nobody who can give you answers is reading it.

:frowning:

ryanb9:
I have ~12 classes at this point and will be adding ~20 more before the project is done. I'm a software developer who is getting into electronics so I don't need too much help with the code aspect but I do need some help in managing the ide with large number of tabs / classes because it is wasting a lot of time currently with how long it takes to view one class and then another. Any ideas?

If you can determine which arduino .h files you need (or can do without by not using things like digitalWrite( )), you can use any file organization you want to and just use avr_gcc to compile and blow off the IDE completely. But I'm not familiar with your development environment so I can't offer specifics.
You do use another editor than the one provided in the IDE? That alone should give you the normal access to files that you are familiar with.

A tree structure for a sketch would be "dreamy" (i.e. a solution/project explorer). To offer a little to the thread, I've tried PlatformIO, and my experience was subpar. I ended up sticking with the Arduino IDE and dealing with tabs.

Instead of putting all of your classes into individual tabs, you could group them.

i.e. Utilities, circuit specific, base classes, etc.

You could also try Visual Studio Code + Arduino.

I am popping by very late to say thanks to Crossroads (upthread) for opening my eyes to the possibilities of tabs!

I have been spending a couple of years scrolling up and down madly, searching for var names and func names, generally getting lost in my source code in one big sketch, and now thanks to the wonders of tabs I have it all much better organised.

I have 12 tabs which at the moment are organised as follows:

  • Main sketch with the project name, contains only includes, defines, global var inits, instantiations, comments.
  • Include file #1 (specific to the Nextion I am using for this project)
  • Include file #2 (specific to the USB HID game controller stuff for this project)
  • Setup
  • Loop
  • Generic utility functions
  • Nextion R/W functions
  • Higher level Nextion screen management functions
  • Sensor reading functions
  • Keypress/dispatch functions
  • HID output functions (Keyboard, Mouse, Joystick)

and lastly a kind of garbage dump tab for experimental or diagnostic functions
that are not normally used.

I keep the compile order reasonable by naming them alphabetically as Crossroads suggested. The early-stage tabs (prior to Loop) have names starting with AA, AB, AC, AD and so on; the function categories just start with A, B, C, D and so on.

Stylistically this may be low-class and ignorant, but it sure is working for me. I used to have a really tedious time checking declarations against runtime references, scrolling back-n-forth, back-n-forth; now, with declarations in separate tabs from functions, I can easily maintain visual contact with my state variables etc.

A button press (Nextion R/W) triggers an Action (Dispatch) which may check a global state struct (main page) and now I can keep all three locations visible without scrolling/searching, tracing the execution path through the code.

I wish I had discovered this feature of the IDE long long ago :slight_smile: but am certainly glad I stumbled on this thread a few days ago. It has changed my life :slight_smile:

Glad it's working, I forget now who told me about the tabs originally. I continue to use them.