bclose

9 – A PROGRAM WITH MULTIPLE FUNCTIONS

Objectives

 

 

    • Learn to work with arrays.
    • Learn how to split a problem into specialized functions.
    • Work with integers and Strings.
    • Train and develop some key skills to program

 

Bill of materials

Arduino Uno  Arduino Uno or equivalent. We can use any other Arduino board in this chapter.
We will also need a PC computer with the Arduino IDE properly installed and set up

SUGGESTING A LITTLE MORE COMPLICATED PROGRAM

 

We have already seen how to define a function. In this chapter we are going to program a sketch that accepts a number from the Serial console and check whether it is prime or not. If the number is not prime, the sketch will calculate its prime divisors.

Usually to solve a complex problem it is good policy to split it into smaller problems that can be solved more easily. In this case we will use at least 3 functions:

 
  • Prime() – check whether a given number is prime, returning true if so and false, otherwise
  • Divisors() – returns the prime divisors for a given number.
  • GetLine() – we are going to define a generic function that reads a string from the Serial console. In this case it will fetch the number we want to check. Later on we will process the string.
 

The idea is that our sketch starts checking whether a given number is prime. If so, good for him, otherwise we will call the Divisors() function to get its prime divisors. And at last we need something that can fetch a number from the Serial console to check it, so we will write another function that allow us to comfortably get that input number.

Notice that almost without writing a line of program, we have decided to how to split the problem into blocks easier to manage and to program. In other words, we have sought a resolution strategy.

 
  • It is a trick. It seems that it have just come into my head but, incredibly, it hasn’t.
  • After thinking for a while and spending more time writing and refining the program, it is a pleasure to present it as if it were easy. It is easy, in fact, but what you can’t guess is the amount of time it takes to do the tests until everything is fitting and tuned.
  • With time and practice (not too much) you will quickly improve your skill to split problems into manageable pieces. It simply requires time and training, and this is something very useful not only for programming.
 

WORKING WITH ARRAYS

 

When we use the Prime() function, that we defined in the previous chapter, as the size of the number grows, the time it takes to determine whether it is prime grows too, since we have to divide it by all the numbers that precede it.

A more effective way to calculate whether a number is prime, is to divide it only by the prime numbers less than it. But for this we need a way to store these prime numbers.

We could run first the sketch Sketch 8_3 to find the first N prime numbers, and if we had some way to save them, we would have a more effective system to decide whether a number is prime or not.

One way to store these numbers is to use an array.

An array is simply a collection of items of the same data type organized as a matrix, and it can have one or several dimensions. Let’s start defining a one-dimensional array.

To define it we can choose two ways:

int series1[5]; //We define an array of 5 integers
int series2[] = { 3,5,6,12, 23};

In the first case we define a one-dimensional array of integers with 5 elements, without assigning values at the time.

In the second case we define a one-dimensional array of integers, without specifying the number of elements because we let C++ the task of counting, and then we initialize it with the values that we pass between the brackets. We say that the array is defined by enumeration.

To assign or read the values of an array we use an index between the brackets. Let’s take a look at this program:

Sketch 9.1
int series2[] = {3,5,6,12,23};            
void setup()
    {
        Serial.begin(9600) ;  
    }
void loop()
    {
       for (int i=0 ; i<5 ; i++)
            Serial.println("Position " + String(i)+ ": "+ String(series2[i])) ;
    }

The program prints the contents of the array crossing its 5 positions.

 
  • Note: the first position of an array is 0, not 1, and the last is equal to the number of elements minus 1, N-1. We say that arrays are zero-based. So, in the second case, series[0] returns the first element, 3, and series2[4], returns the last element, 23.
 

The following sketch shows a very dangerous mistake difficult to detect:

Sketch 9.2
int series2[] = { 3,5,6,12, 23} ;
    for (int i=0 ; i<99 ; i++)
         Serial.println("Position " + String(i)+ ": "+ String(series2[i])) ;

One could expect that C ++ will generate an error, as we defined an array of 5 elements and we tried to accede 100. But C ++ surprises us again because it returns correctly the first 5 values and then keeps on reading consecutive memory locations, as cool as a cucumber, as if it had sense.

 
  • C ++ expects us to take care of this, so be careful. 

Finally, we should mention that we can handle arrays with several dimensions:

             Int ChessBoard[ 8, 8 ] ;

In this example, the two-dimensional ChessBoard[] array represents the squares of a chess game and the value contained in each position corresponds to a piece located in that square.

IMPROVING THE PRIME() FUNCTION

 

If you run the program Sketch 8_3, you will see a list of the prime numbers up to 1024 (or up to the number you wish, just modifying the maximum value) and after selecting them with the mouse you can copy and paste the values into the IDE to create an array. (Prog_9.3):

int P[] =
{          2,             3,             5,         7,          11,         13,         17,         19,
          23,            29,            31,        37,          41,         43,         47,         53,
          59,            61,            67,        71,          73,         79,         83,         89,
          97,           101,           103,        107,        109,        113,        127,        131,
          137,          139,           149,        151,        157,        163,        167,        173,
          179,          181,           191,        193,        197,        199,        211,        223,
          227,          229,           233,        239,        241,        251,        257,        263,
          269,          271,           277,        281,        283,        293,        307,        311,
          313,          317,           331,        337,        347,        349,        353,        359,
          367,          373,           379,        383,        389,        397,        401,        409,
          419,          421,           431,        433,        439,        443,        449,        457,
          461,          463,           467,        479,        487,        491,        499,        503,
          509,          521,           523,        541,        547,        557,        563,        569,
          571,          577,           587,        593,        599,        601,        607,        613,
          617,          619,           631,        641,        643,        647,        653,        659,
          661,          673,           677,        683,        691,        701,        709,        719,
          727,          733,           739,        743,        751,        757,        761,        769,
          773,          787,           797,        809,        811,        821,        823,        827,
          829,          839,           853,        857,        859,        863,        877,        881,
          883,          887,           907,        911,        919,        929,        937,        941,
          947,          953,           967,        971,        977,        983,        991,        997,
          1009,        1013,          1019,        1021
 }  ;

We have defined an array enumerating its elements, separated by commas, between braces.

 
  • It is important to realize that after pasting the output provided by Sketch 8_3, we have deleted the comma after 1021, because unless you do it you will see a syntax error when defining the array.
  • Note that there is a semicolon after the closing brace of the array. Although it is written between braces it is an assignment statement rather than a function definition.
  • When we define an array by enumeration, if the number of elements is high, we can lose sight of how many elements it contains. So if we need to calculate the number of elements it contains, we can use the sizeof() function:
                                          int size = sizeof(P) / sizeof(int);
  • Where P is our array. We divide the size of our array by sizeof(int), the size of an integer, because we have defined P as an array of integers. And in this case, it will return a size of 172 elements.
 

Now we have just to divide the number we want to check by all the elements of the array less than it:

bool Prime(int x)
{
    int index = 0 ;
    while (P[index]  <  x)
        { if ( x  %  P[index++] == 0)
              return(false);
        }
    return(true);
}

We iterate the values stored in the P array by using the index variable, that is increased in each iteration, until we get a prime number greater than the value we want to check.

THE DIVISORS() FUNCTION

 

This function will iterate the elements of the array P as long as they are less than the number we want to check. That is, we try to use all the possible divisors of the given number. If we find prime divisors, we store them in an array that we have called Div[], defined to contain a maximum of 32 elements:

int Div[32] ;
int Divisors(int x)
    {       
        int index = 0 ;                      //Points to the array of primes P[]
        int pos = 0 ;                        //Points to the array of divisors Div[]
        while ( P[index] < x)
             { 
                 int k = P[index++] ;
                 if ( x % k == 0)
                 Div[pos++]= k ;             //We store the divisor in the array Div[]to use it further
              }                              
        return(pos);                         //We return the number of divisors 
    }

Whenever we want to get the divisors of a given number, we just have to iterate Div[].

 
  • It is important to understand that both, Prime() and Divisors() functions, iterate the array of prime numbers until the selected value exceeds the value of the number to be tested. If the number to be tested is greater than the maximum prime contained in P[], we might get strange results, as we will read more items than we have defined.
  • This method to find divisors is only valid for numbers less than 1024, in our case, because this is the maximum number for which we have calculated its prime divisors. A number won’t be prime if the Prime() function says that, because it will find its prime divisors. But the Prime() function could wrongly state that a number is prime if its divisors are greater than the maximum prime stored in P[].
 

THE GETLINE() FUNCTION

 

Although we discussed that we can use the parseInt() function, provided by Arduino C++, to get a value from the Serial port, it has a disadvantage. If the parseInt() function does not receive an input after a while (very short), it ends and returns 0, so we would have to continuously check the returned value.

So let’s write a general purpose function that allows us to read a string from the Serial port, without returning it, until a finalizing character is received, the intro character. In fact we saw this, but not defined as a function in the chapter: Communication with the outside world.

String GetLine()  
    {
        String S = "" ;
        if (Serial.available())
            {
                 char c = Serial.read(); ;
                 while ( c != '\n')                           //Loops until the character is equal to intro
                       {     
                             S = S + c ;
                             delay(25) ;
                             c = Serial.read();
                       }
                 return(S) ;
            }
    }

We define the GetLine() function as String Data Type, because we want it to return a text string. We check whether there is some data available in the Serial port, and if so, we build a String S adding each of the characters we read from the Serial port, until we find an intro character.

When we find the intro character, the exit condition of the while loop statement is met and the function ends, returning the built string without the intro character.

 
  • Normally we should check whether anything is available in the Serial port before calling the GetLine() function, although it could seem to be redundant because we also check whether some data is available in the Serial port inside the GetLine() function.
  • But if we called GetLine() without checking before whether some data is available and the function were not able to control it, we could be caught inside the function until something were written (and finalized with an intro character) in the Serial console and then it could not be easy to understand the problem.
  • We have included again a delay of 25 milliseconds in the while loop to make sure that Arduino can’t read more characters, at a 9600 bps data rate, before reaching the next character. If the data rate is 115200 bits per second or more, this delay can be suppressed.
 

THE MAIN PROGRAM

 

We can now write the main loop () function, which calls the functions that we have defined throughout this chapter, in order to determine whether a number read from the Serial port is prime or not and if not, to show us its prime divisors.

It could be something like this:

Sketch 9.3
void loop()
    {
        if (Serial.available())
            {
                String s = GetLine();
                int i = s.toInt() ;          //As we expect a number, we convert the text to a number 
                if ( Prime(i))
                     Serial.println(String(i) + " is prime.");
                else
                     {
                        Serial.println(String(i) + " is not prime.");
                        Serial.println("These are its prime divisors:  ");
                         int j = Divisors(i);                 //We store the number of divisors found
                         for (int n =0 ; n<j ; n++)            //We print the divisors of the Div[]array
                              Serial.print(String(Div[n]) + ",\t");
                         Serial.println(" ");                  // We insert a line break
                     }
            }
    }

We start checking whether there is some data available in the Serial port and if so, we call GetLine() to get it.

As GetLine() returns a String Data Type, we use the standard function provided by Arduino C ++, toInt(), to convert the String Data Type to an integer Data Type.

After that, we call Prime() to check this number. If it is prime, we simply print a confirmation message. Otherwise we call Divisors() to find and store in the Div[]array its prime divisors.

When Divisors() ends, returns the number of divisors found so that we can print them using a single loop.

SUMMARY

 

 

    • We have learnt how to work with one-dimensional arrays in order to read their contents so as to modify them.
    • We have used this sketch as an excuse to show how we can solve complex problems, by splitting them into simpler ones that can be solved using more straightforward functions.
    • This is undoubtedly one of the key skills needed to be a competent programmer, and like everything in life requires practice and keep working.
    • We have also defined the general purpose function GetLine(), that we will further use..

 

 

No Comments

Give a Reply