How to split one sketch into few Tabs and how to keep the relation between them?

Hi,

  1. I checked some multi sketches make a 'main' and few Tabs which been set as 'class' .
  2. Some put a "#pragma once" on the top of Tabs.
  3. Some cann't figular out which one is 'main' like the picture attached.
    How should I do?
    Thanks
    Adam
1 Like

It's not clear what you mean by that statement. Please post a link to them.

This is one technique to prevent multiple inclusions of a header file in the same translation unit:
https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html#index-_0023pragma-once

If #pragma once is seen when scanning a header file, that file will never be read again, no matter what. It is a less-portable alternative to using ‘#ifndef’ to guard the contents of header files against multiple inclusions.

It's an alternative technique to the standard way of doing, which is an include guard:

It's not clear what you mean by that. Please provide a detailed explanation.

My recommendation for you at this stage in your learning journey is that you use only standard Arduino language sketch tabs (.ino files). You get these by not adding a file extension when naming a tab. This makes splitting a sketch into multiple tabs very easy without requiring you to make any changes at all to the code.

Before compiling, the Arduino build system concatenates all .ino files into a single file, following the order the tabs are shown in the IDE. This order is the file that matches the sketch folder name followed by all the other files in alphabetical order.

I'll provide an example:

This single file sketch with the folder structure:

SomeSketch
|_ SomeSketch.ino

and the code:

SomeSketch.ino

void setup() {
}

void loop() {
  someFunction();
}

void someFunction() {
}

Is identical to this multi-file sketch with the folder structure:

SomeSketch
|_ Functions.ino
|_ SomeSketch.ino

and the code:

SomeSketch.ino

void setup() {
}

void loop() {
  someFunction();
}

Functions.ino

void someFunction() {
}

While it is possible to add tabs to the sketch with other file extensions:

  • .h (header)
  • .cpp (C++)
  • .c (C)
  • .S (assembly)

This is a more complex project and doesn't really provide any benefits to a newer user.

2 Likes

Thank you for nice.
Actually what I really like to know is how to separate a long sketch into multi-Tan structure。are there any example?

A long sketch implies that there are several functions, or at least I hope so. In which case move some of them into a new tab as an experiment

1 Like

Well,
here is an example of multi-tabs sketch, I didn't find a 'setup' or 'loop', which one is a main? whats the order of the sketch running?
Thank for help.

Marlin.ino (1.9 KB)

Where ?

Try this sketch

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  Serial.println(sum(3, 4));
}

void loop()
{
}

int sum(int x, int y)
{
  return x + y;
}

Now move the sum() function into its own tab named tab1 and upload the sketch

It still works because the IDE has combined all of the tabs with a .ino extension into a single file before compiling them

1 Like

Thanks.
The code attached now, there are too many Tabs to post.

This is a very complex sketch, so not the best introduction to multi-file sketches. But it is a very important and interesting sketch.

The Marlin sketch consists of hundreds of files. The average user of Marlin only wants to update their 3D printer firmware and doesn't have any real interest in the source code. So presenting them with hundreds of tabs of complex source code that they need to sift through to find the one or two configuration files they actually need is not very user friendly.

For this reason, the Marlin developers took advantage of another sketch feature, which is the src subfolder. Any source files you put in a subfolder in your sketch folder named src will be compiled. But these files are not shown in the Arduino IDE as tabs. The root folder of the Marlin sketch only contains four sketch files:

  • Marlin.ino - the primary sketch file, which consists only of a comment providing the license information and an introduction.
  • Configuration.h - the most commonly used configuration parameters
  • Configuration_adv.h - the more advanced configuration parameters
  • Version.h - a template file that can be adapted by 3D printer vendors to identify their modified versions of Marlin.

All the actual code is in the files under the src subfolder. src/MarlinCore.cpp contains the setup() and loop() functions.

It's not clear what you mean by "a main". Please provide a detailed explanation.

Just the same as usual: setup() is called once when the program starts, then loop() is called over and over again forever.

1 Like

Thanks.
The 'main' may not a proper word here, I mean the one sketch that included: setup() & loop().

  1. I didn't find setup all through the sketch, don't have setup()?
  2. can I declare all variables in one sketch, and used for all tabs by public?

The problem with the word "main" is that C++ programs (the Arduino programming language is a superset of C++) use a function named main. Arduino programs substitute the setup and loop functions in place of main because this reduces the amount of code required to make a working sketch. But the Arduino sketch is compiled as C++ and the main function is still there, only hidden away in the Arduino core library. However, it is possible to define your own main function in the sketch if you don't like the setup/loop paradigm. So when you make vague mentions of "main" in relation to code, it is a bit ambiguous whether you are talking about that main function or not.

The sketch is all the code files in the folder. I think here you mean "file" rather than "sketch".

It's here:

It's not clear what you are asking. Please provide a detailed explanation.

1 Like

If you're going to go the multi-file / multi-tab route, I'd recommend the method that's been in use pretty much since K met R (with updates for C++ classes, etc). See My Reply #5 Here. Note that this does not cover C++ templates.

1 Like

Thanks.
Can you make this sketch as Tabs?

/**************************************************************************
 * 
 * Arduino autoranging AC/DC voltmeter.
 * Voltage and frequency are printed on 1602 LCD screen.
 * This is a free software with NO WARRANTY - Use it at your own risk!
 * https://simple-circuit.com/
 *
 *
 *************************************************************************/

#include <LiquidCrystal.h>   // include Arduino LCD library
// LCD module connections (RS, E, D4, D5, D6, D7)
LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

// define autoranging channel pins
#define CH0  2
#define CH1  3
#define CH2  4
#define CH3  5

const uint16_t Time_Out = 50000,  // time out in microseconds
               Periods  = 10;     // number of periods of measurement (for AC voltage only)

// variables
byte ch_number;
const uint16_t res_table[4] = {2444, 244, 94, 47},  // voltage divider resistances in tenths kOhms
               total_res = 22444;                   // total resistance in tenths kOhms
uint16_t current_res;
volatile byte per;

void setup(void)
{
  pinMode(CH0, OUTPUT);
  pinMode(CH1, OUTPUT);
  pinMode(CH2, OUTPUT);
  pinMode(CH3, OUTPUT);

  lcd.begin(16, 2);     // set up the LCD's number of columns and rows
  lcd.setCursor(1, 0);
  lcd.print("Voltage:");
 //// lcd.print("Hello!");

  ch_number = 0;
  ch_select(ch_number);

  // ADC and analog comparator configuration
  ADMUX  = 0x03;
  ADCSRA = 0x87;
  ADCSRB = (0 << ACME);  // select AIN1 as comparator negative input
  ACSR   = 0x13;         // turn on analog comparator

}

// analog comparator ISR
ISR (ANALOG_COMP_vect)
{
  byte count = 0;
  for(byte i = 0; i < 50; i++) {
    if ( ACSR & 0x20 )
      count++;
  }

  if(count > 48)
    per++;
}

// main loop
void loop()
{
  bool dc_flag = 0; // DC voltage flag bit
  int32_t sum = 0;  // sum of all readings
  uint16_t n = 0;   // number of readings (samples)

  ACSR = (1 << ACI);   // clear analog comparator interrupt flag
  ACSR = (1 << ACIE);  // enable analog comparator interrupt

  uint32_t current_m = micros();  // save current millis
  byte current_per = per;         // save current period number
  while ( (current_per == per) && (micros() - current_m < Time_Out) ) ;

  if( micros() - current_m >= Time_Out ) {  // if there's time out event ==> voltage signal is DC
    dc_flag = 1;
    for (byte i = 0; i < 200; i++) {
      ADCSRA |= 1 << ADSC;   // start conversion
      while(ADCSRA & 0x40);  // wait for conversion complete
      int16_t an = (int16_t)(ADCL | (uint16_t)ADCH << 8) - 511;
      sum += an;
      n++;         // increment number of readings
      delay(1);
    }
  }

  else {   // here, voltage signal is AC
    current_m = micros();  // save current millis()
    per = 0;
    while ( (per < Periods) && (micros() - current_m < (uint32_t)Time_Out * Periods) ) {
      ADCSRA |= 1 << ADSC;   // start conversion
      while(ADCSRA & 0x40);  // wait for conversion complete
      int32_t an = (int16_t)(ADCL | (uint16_t)ADCH << 8) - 511;
      sum += sq(an);  // sq: square
      n++;            // increment number of readings
    }
  }

  ACSR = (0 << ACIE);  // disable analog comparator interrupt
  uint32_t total_time = micros() - current_m;  // used to claculate frequency

  // voltage calculation
  float v;
  if(dc_flag)   // if voltage signal is DC
    v = (4 * sum)/n;   // calculate Arduino analog channel DC voltage in milli-Volts

  else  // here voltage signal is AC
    v = 4 * sqrt(sum/n);   // calculate Arduino analog channel RMS voltage in milli-Volts

  // claculate actual (input) voltage in milli-Volts (apply voltage divider equation)
  v = v * (float)total_res/current_res;
  v /= 1000;  // get voltage in Volts

  uint16_t v_abs = abs(int16_t(v));
  if( (v_abs >= 10 && ch_number == 0) || (v_abs >= 100 && ch_number == 1) || (v_abs >= 250 && ch_number == 2) ) {
    ch_number++;
    ch_select(ch_number);
    delay(10);
    return;
  }

  if( (v_abs < 220 && ch_number == 3) || (v_abs < 80 && ch_number == 2) || (v_abs < 8 && ch_number == 1) ) {
    ch_number--;
    ch_select(ch_number);
    delay(10);
    return;
  }

  char _buffer[8];
  lcd.setCursor(0, 1);
  if( v < 0)
    lcd.print('-');
  else
    lcd.print(' ');
  if(v_abs < 10)
    sprintf( _buffer, "%01u.%02u", v_abs, abs((int16_t)(v * 100)) % 100 );
  else if( v_abs < 100)
    sprintf( _buffer, "%02u.%01u", v_abs, abs((int16_t)(v * 10)) % 10 );
  else
    sprintf( _buffer, "%03u ", v_abs );

  lcd.print(_buffer);
  if(dc_flag)
    lcd.print("VDC        ");
  else {
    lcd.print("VAC ");
    // calculate signal frequency in Hz
    uint32_t period_time = total_time/Periods;
    float freq = 1000000.0/period_time;
    sprintf( _buffer, "%02u.%02uHz", (uint16_t)freq % 100, (uint16_t)(freq * 100) % 100 );
    lcd.print(_buffer);
  }

  delay(500);    // wait half a second

}

void ch_select(byte n) {
  switch(n) {
    case 0:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, LOW);
      break;
    case 1:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, LOW);
      break;
    case 2:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, LOW);
      break;
    case 3:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, HIGH);
  }
  current_res = res_table[n];
}

// end of code.

Thanks.
'It's not clear what you are asking.'
I mean if I have a 4 tabs sketch, each tab use some variables, can I declare all variables in just one sketch and indicate them as public?

This tutorial covers breaking your sketch up into tabs and progressing on to a 'private' library
Simple Arduino Libraries for Beginners

1 Like

Great!
Thank you.

I'll state again that I strongly recommend against attempting to use .h and .cpp tabs at this time. This is difficult enough already without introducing all those additional concepts at the same time.

1 Like

Thanks.
I'll keep all Tabs .INO , how to call them?

Make sure to note that the file extension for Arduino sketches is .ino (lower case), not .INO.
You don't really need to think about the file extension of these files while working with the Arduino IDE because it automatically adds the extension to the file when you create a tab. So if you create a tab with the name "SomeFunctions", then the file added to the sketch folder is actually named SomeFunctions.ino.

It's not clear to me what you mean by that. Please provide a detailed description.

If you are asking how we refer to files with a .ino file extension specifically when we are talking about them, I'm not sure there is a standard. I just refer to them as ".ino files" ("dot eeno files" when spoken). You could also call them "Arduino language files", but that might result in conversations getting side tracked into discussions about whether Arduino is a programming language.

1 Like

Did you try what I suggested in reply #6 ?
You do not need to do anything specific to use the functions in a tab when used like this

As long as the tabs have the .ino extension, nothing special has to be done to make your global functions and variables 'public' to the other tabs. All of the .ino files are put together into one file before compiling.

1 Like