©KIPR, 2019

KISS IDE User Manual for C


Introduction

The KISS Institute for Practical Robotics (KIPR) was founded in 1994 to promote the use of assistive and educational robotics. Initial educational robotics programs for adults led to the development of the Botball Educational Robotics Program in 1999, which has become a major area of focus for KIPR. KIPR has led the development of a long series of robotics controllers for use in its educational robotics programs since that time, the most recent being the KIPR Robotics Controller, or KRC.

The KISS IDE is a web-based Integrated Development Environment (IDE) implemented on the KRC. Any web capable device that has wifi or ethernet capability can bring up the KISS IDE and use it to develop software projects for use with the KRC. In constructing software projects for the KRC, the user can opt to use a professional programming language such as C or Python. Libraries of special purpose functions allow use of sensors, motors, servos, a camera, and other equipment the user has connected to the KRC. The KRC has a color touch screen, with analog and digital I/O ports, DC motor ports, servo ports, USB ports, and built in sensors such as an accelerometer, a gyro, and a magnetometer. The underlying operating system for the KRC is Linux. A user interface, the KISS UI, appears on the touch screen when the KRC is turned on. The KISS UI provides easy access to a number of useful KRC capabilities, which include running software projects developed using the KISS IDE.

This is the on-line manual for the development of C programs using the KISS IDE, and does not address other programming languages supported by the KISS IDE. The full ANSI C specification is implemented on the KRC and can be accessed through the KISS IDE. While C is the systems programming language most widely used by programming professionals, it is readily accessible by novices, regardless of age. The KISS IDE is designed to make systems programming considerations transparent for beginners, but without putting constraints on those with advanced programming knowledge. For information about the C programing language, including history and basic syntax, see the Wikipedia article C (programming language). For a more complete tutorial and guide for C Programming visit CProgramming. The landmark reference book on C, The C programming Language (2nd. edition, available from Amazon) by Kernighan and Ritchie must also be mentioned, since its authors are among the original developers for Unix and C. Their book is also the progenitor for much of the material in this documentation and other programming reference works. The Botball community website also has several articles about programming and a user forum where questions from Botball participants can be posted to the Botball community. Other manuals available through KIPR have system specific information and program examples not included in this manual.

The primary purpose of this manual is to describe the C functions provided for the KRC and their use. This manual also includes a basic introduction to programming in C for those who already have experience with some other programming language or for those who need a refresher for C. To learn more about programming in C, consult one of the many books or websites that provide C references and tutorials such as the ones suggested above.

KISS IDE Interface

The KISS IDE is used to manage software projects, especially those produced for the KRC. When working on a project the KISS IDE is used to construct new (unsaved) project files and for editing old ones. To use the KISS IDE, the user must first turn on their KRC to bring up the KISS UI. The "About" button on the KISS UI provides the information needed to connect the KRC wirelessly to a lap top. Alternately, an ethernet cable can be used, but requires determining the IP address provided by the computer or router being cabled to, for which the reader is advised to contact someone with knowledge of Linux. Once the KRC has been connected with a computer, the user only needs to start up a web browser like Firefox or Chrome and supply the IP address for their KRC; e.g., 192.168.125.1:8888 will typically work for wireless, where 192.168.125.1 is the IP address. This brings up the KISS IDE main menu, with various options. Clicking "KISS IDE" brings up the IDE for the "Default User" in the "Project Explorer" panel.

Beginners may opt to use the KISS IDE as the "Default User". The drop down arrow beside "Default User" lists other users who have been added. The "+ -" options beside the drop down arrow allow adding or deleting users. When a user is selected, all projects for the user that are on the KRC are listed. Selecting a project shows the source files for the project and selecting a source file displays it for editing in the editing panel. For programs that use multiple source files, return to the main menu, select "User Preferences", then select the "User" and change the "Interface Mode" to "Advanced".

To start a new project, click the "+ Add Project" line on the "Project Explorer" panel. Name the project and select the programming language. This will always put an appropriate template in place for the programming language, which the user modifies as needed for their project. Various editor options are provided. The "Save" button is highlighted in red if the editor has been used to make modifications, and turns green if the file remains as first loaded. The "Compile" button sends the project to the compiler. The compiler report is displayed on the editor panel, showing either success or a list of errors and warnings, where the result of any error is an inoperable program. The "Run" button will run a successfully compiled program and mimic the KRC display on the editor panel (except for graphics and print in place). The "File" button allows saving the specific file in the editor panel to the user's computer, useful for backing up work in progress. Similarly, the "Project" button archives the entire project to the user's computer.

Keep in mind that the KRC is a very powerful small computer with sufficient storage to hold hundreds of projects. When the KRC is turned on, in addition to the KISS UI it starts a web server that allows the user to access the KRC from their own computer using the KISS IDE. The "Setting" button on the KISS UI allows the user to "Hide UI" to expose the underlying Linux desktop, which has the same kind of facilities that most users are already familiar with from their own computer experience. This includes access to USB thumb drives (via the file manager), another way to archive project information absent access to the KISS IDE.

Programming in C

Following a Quick C Tutorial introduction, more detailed sections are provided which describe

The C programming information provided is sufficient for most purposes, but a C programming reference such as those noted above should be consulted where more detail is needed or where elements of C not covered (such as functions with a variable number of parameters or function pointers) are needed to solve some programming issue.

A Quick C Tutorial

C is a programming language, that unlike natural languages has rigid rules of syntax for determining if a programmed task can be successfully converted to the binary code used to operate a computer. Independently developed software called a compiler (or interpreter) is used to make the conversion. The result is retained and used to have the computer do the task specified by the program. Software called an operating system is used for this purpose. The operating system for the KRC is Linux.

A C program consists of one or more function definitions along with user specified data structures, and typically utilizes pre-compiled code retained in function libraries. The term function is taken from mathematics, where an algebraic expression such as x² + y² + 1 defines a function with parameters x and y. The expression produces a unique value when numbers are substituted for x and y. Similarly a C function requires 0 or more parameters and returns a value much like a function in mathematics (but with one exception, a C function in doing its task may return nothing). In order for the programmed task to be a C program, one of its function definitions must be named main to determine which function starts the task.

The following is a simple C program which has a single defined function named main and which utilizes the library function printf. The definition of a C function should always include apecification of what type of value is being returned to distinguish between types of data such as numbers and text. For the C function main the returned value is to the operating system, which expects it to be an integer (type int). As shown in the program below, the return data type is followed by the function name (in this case, main) and its parameter list. The parameter list is delineated by parentheses with the parameters separated by commas (note that in this case the list is empty, but the parantheses are still needed to show that). The programming code that specifies what the function is to do is delimited by curly braces ({}), indicating where the specification begins and where it ends. Comments are normally included to document a function's purpose and expected behavior, and are ignored in the compilation process.

/* If system I/O functions not automatically included add the line
#include <stdio.h>
*/
int main()
{
    printf("Hello, world!\n"); // simple example
    return 0;
}

Text between /* and */ forms what is termed a bracketed comment, which can extend over multiple lines. Text that starts with // forms a single line comment which continues only to the end of the line. Commenting program code is a good habit to form, especially considering it has no impact on program performance.

All C functions must have a return data type. While main does not return a value to another function, it is expected to return an integer to the operating system (not relevant to this discussion, but usually 0 is used to indicate normal program end), and so has return type int. In C, a function does not have to return a value, which is specified by return type void.

Another common numeric data type is double, which represents (double precision) floating point numbers used for non-integer calculation. There are additional basic (or ordinal) data types, and user defined data types derived from basic data types may also be employed.

Pre-compiled functions in the standard C library such as printf need to be declared if used in a program. The system environment may or may not have been set up to #include the specifications for the library functions (in which case a preprocessor directive such as #include <stdio.h> may be needed to avoid compiler warnings and possible program failure).

To reiterate, the function's name immediately follows the function's return type specification (in this case, main). Next, in parentheses, are any arguments (or inputs) to the function. main has none, signified by an empty set of parentheses.

The open curly-brace ({) that follows the argument list signifies the start of the actual function code. Curly-braces are used to structure a program as blocks of code. For C, so-called "white space" consists of one or more of any combination of characters such as spaces, returns, new lines, and tabs, which is collapsed to a single generic white space character during compilation. For this reason, white space can be used for things like indentation that improve program readability. In particular, the KISS IDE editor automatically applies white space readability conventions to enhance files as they are edited.

The body of the function consists of a block consisting of a series of C statements. A C statement specifies a data structure or some specific action to be taken and is terminated with a semicolon (;). The simple example program has a single statement,

printf("Hello, world!\n");

The action this statement specifies is calling the C library printf function to format and print the message Hello, world! to the project target. The '\n' directs printf to "start a new line" (in effect ending the current line and positioning for subsequent printing to start on the next line). When printing reaches the bottom of the KRC display, any additional lines printed cause the display to scroll up. Since calling the printf function is a C statement, it has to be ended with a semicolon (;). A common error made by beginning C programmers is omitting the semicolon that is required to end each statement.

The closing curly-brace (}) for the main function's block structure concludes its definition.

If the compiler issues a warning that printf is being used with an implicit declaration, then prefacing the program code with the preprocessor directive

#include <stdio.h>

will ensure that the C preprocessor includes the declaration in preparing the code for compilation. If it has already been included, the added statement is redundant and will be ignored by the C preprocessor. The ".h" appended to the file name indicates a "header" file of specifications for functions in a C function library.

As a second example exhibiting features of C, the following program code defines a function named square which returns the mathematical square of an integer. In this case the function does not represent a complete C program, since a function named main for using it has not been defined yet.

int square(int n)
{
    return n * n;
}

The function is declared with return type int, which means that it will return an integer value.

The function name (square) is followed by its parameter (or argument) list in parentheses, where parameters are separated by commas if there are more than one (there is only one parameter in this case). The parameter for square is specified to be an integer and is named n. The data type for each parameter is declared in the same manner as declaring the return data type of the function.

When a function declaration specifies parameters, the names representing the parameters are "local" to the function, or stated another way, they are valid only within the "scope" of the function (they only have meaning within the function's block of code). For this reason, parameter names used within the scope of a function will not cause a semantic conflict should their names duplicate those local to some other function.

The "scope" for the function square is what takes place within the block structure defining its actions (i.e., the contents within the curly braces surrounding its program statements). In this case, the function's actions consist of a single statement; namely, the return statement. The action of the return statement is to exit the function, assigning the function's return value to be what is specified by the return statement, in this case the result of the computation n * n (i.e., n²).

Except where grouped by parentheses, expressions are evaluated according to a set of precedence rules associated with the various operators used within the expression. In this case, only the multiplication operator * is employed, so operator precedence is not an issue.

For a third example, the following program code defines a function which performs a function call to the square program.

#include <math.h>
double hypotenuse(int a, int b)
{
    double h;
    h = sqrt(square(a) + square(b));
    return(h);
}

The parameter list for this function has two parameters, each with integer data type. Additionally, its block structure includes a statement specifying a "working variable" h. h is "local" to the block in which it is specified. In compiling the block of program code, the compiler assigns each such variable a location in memory to hold its value. The term variable is used since its value may undergo change as a program runs (e.g., a statement such as h = h+1; adds 1 to the value in memory for h and then stores the new value at h's location. Parameters are treated as local variables within the function's primary block and so are often referenced that way.

Since the function needs to calculate a square root, the function sqrt located in C's math library is used. Just as #include <stdio.h> ensured that the printf function was available for use in the earlier example, #include <math.h> ensures that the math function sqrt is available for this function to use. The return value and parameter for sqrt are specified to be floating point (or fractionsl) numbers.

The data type for h is given as floating point since sqrt returns a floating point value. If the data type for h was specified to be integer, the fractional part of any floating point number assigned to it would be lost. In general, as is the case for the variable h, any local variables used within a program block (indicated by a set of curly braces) are specified at the beginning of the block. If a variable name is used that is not specified within a block, the compiler looks for its specification successively in containing blocks.

The value assigned to h is the value returned by calling the sqrt function to calculate the mathematical square root of its argument. The hypotenuse function uses the square function defined earlier to determine the hypoteneuse as the sum of the squares of the lengths of the two legs of a right triangle; i.e., a² + b².

Since both the return value and the parameter for sqrt are specified to be floating point, what happens if sqrt is called with an integer argument instead of floating point? Answer: C will automatically coerce the integer to a floating point value (which uses a format involving both an exponent and a mantissa). If C's automatic coercion does the "wrong" thing, it can be bypassed by specifying how the value is to be coerced, which is accomplished by preceding it with the data type in parentheses into which it is to be converted; e.g., (double)(square(a) + square(b)). For numbers, automatic coercion between integer and floating point usually does the "right" thing.

The hypotenuse function is concluded by returning the value of h.

The functions square and hypotenuse still do not constitute a program since a main function that uses them still needs to be defined. A complete program using these two functions follows:

/* Extended example: C Programmer's Manual */
#include <stdio.h>
#include <math.h>
int square(int n);
double hypotenuse(int a, int b);
int main()
{
    printf("Hypotenuse of a 3,4 right triangle is %d\n",(int) hypotenuse(3,4)); // coerce hypotenuse to int
    return 0;
}
int square(int n)
{
    return(n * n);
}
double hypotenuse(int a, int b)
{
    double h;
    h = sqrt(square(a) + square(b));
    return h;
}

Program behavior is described by main, which calls the C library function printf, which in turn calls the function hypotenuse to obtain the parameter value, and hypotenuse in turn calls the function square in order to do its calculation. These calls result in the calculation and printing of the length (truncated to integer) of the hypotenuse of a right triangle by supplying the lengths of its two sides to the hypotenuse function. The program also illustrates that the number of parameters for printf is variable since in this case it has 2 parameters. The value of each parameter after the first is substituted into the display string in place of its corresponding "%" code where "%d" is a % code used for integers.

Note also that the floating point value supplied to printf from hypotenuse is coerced to the integer data type, since otherwise printf will print its floating point return value as if it were an integer, a semantic error (unlike arithmetic expressions, printf does not do automatic coercion). For printf to display floating point format, the printf "%d" code would need to be replaced by "%f".

The two C statements

int square(int n);
double hypotenuse(int a, int b);

provide C "prototype" specifications for the square and hypotenuse functions used in the program. Similar prototypes need to be supplied for the standard C library's printf function and the C math library's sqrt function. Function prototypes are needed for the compiler to set up the machine level structures necessary for using the functions they specify. The preprocessor #include statements provide the prototypes for the functions in the libraries specified. The preprocessor statements

#include <stdio.h>
#include <math.h>

have the C preprocessor provide the needed prototypes (if not needed, the preprocessor will skip these statements; i.e., it is usually a good habit to put them in anyway). Examples of other commonly used system header files are <string.h>, <stdlib.h>, <stdarg.h>, and <time.h>. The C preprocessor is a separate facility that saves programming effort by using preprocessor directives to insert commonly needed elements into a program before its compilation. The KISS IDE provides means for users to create their own header files. Preprocessor directives other than #include are addressed later.

The declaration of a function's prototype or it's actual definition needs to precede the function's first use in program code so that the compiler will be able to correctly translate the statement which calls the function. For this example, since the definitions of the square and hypotenuse functions follow the definition of the main function, their prototype declarations need to be included before the definition of main. Absent a declaration for a function, the compiler will issue an implicit declaration warning for where it first encounters the function, the results from which may be a subsequent compilation error or incorrect results when the program is executed.

This concludes the quick C tutorial.

Data Objects

Variables and constants are basic data objects used in a C program. More complex data structures and data types based on basic data objects and types can also be defined. Specification statements (such as the one used for the variable h in the hypotenuse program above) are used to define the variables the programmer wants to use in a program. A specification statement may set a variable's initial value as well as specifying its data type.

Variables

Variable names are formed from combinations of lower case letters, capital letters, the decimal digits (0-9), and the underscore character ('_'). There are two restrictions that must be observed in forming the name:

Variables which are specified as function arguments or which are defined within program blocks are called local variables. Their scope is limited to the structure in which they are defined. Variables defined at the same level as the main function and other functions (i.e., not inside any function's block structure) apply across all functions used by the program. These are called global variables and are used for data structures accessed by more than one function, or for function to function message passing (especially for functions operating in parallel threads).

Since functions and global variables are defined at the same level, they must have unique names. The name of a function or a global variable can be used as the name of a local variable, in which case the local use takes precedence; i.e., the scope of the local variable supercedes that of a function or global variable with the same name.

Declaration

In C, a variable is declared by a specification statement that provides its data type, and optionally its initial value. Declarations for global variable are normally grouped before the definition of main. Declarations for local variables are normally grouped at the beginning of the block structure in which they are used. The general form of a variable declaration is one of the following:

<data-type> can be a basic (ordinal) data type such as int, double, char, or a composite data type such as a pointer data type or a user-defined data type. User-defined data types are usually based on structures or on enumerated sets. In particular, a struct definition creates a user-defined data type struct <struct-name>, typically simplified by using typedef to provide the struct <struct-name> data type with a more digestible name; e.g.,

typedef struct cplxtype {
    int a;
    int bi;
} complex;

A function can then use the name "complex" for the "struct cplxtype" data type to specify that a variable has the structure specified; e.g.,

complex c;

or equivalently

struct cplxtype c;

where the components of c are referenced by c.a and c.bi.

struct defines a data type based on a structure. An "enumerated set" is another form of user-defined data type specified by the directive enum discussed below.

Local and Global Scopes

If a variable is declared within a function, or as an argument to a function, its binding is local, meaning that the variable is available for use only within the context of that function definition. If a variable is declared outside of a function, it is a global variable and is available for use within any function employed by the program, including functions which are defined in files other than the one in which the global variable is declared. If a global variable represents a complex data structure whose definition would clutter up the main program file, it is sometimes advantageous to define it in a separate file to be included by the C preprocessor in preparing the program code for compilation.

Variable Initialization

The initial value of a local or global variable can optionally be specified in its declaration. If no initialization value is given, the value is indeterminate. When a global variable is initialized, the initialization value must be a constant. In contrast, local variables may be initialized to the value of arbitrary expressions including any global variables, function calls, function arguments, or local variables which have already been initialized.

The compilation process translates a program into machine executable form, initializing global variables within the resulting execute module as specified in global variable declarations. Local variable initialization doesn't take place until program execution enters the block in which the local variable is defined. The initialization occurs every time program execution enters the block, excepting local variables whose declaration is preceded by the word static. For a local variable declared to be static, initialization only takes place the first time the block defining it is entered. A static local variable retains the last value assigned to it whether or not program execution is in its block.

When applied at the global level, a static specification limits the scope of a global variable or subordinate function to the source file in which it is defined. This serves the purpose of allowing a pre-compiled library function to define global variables and subordinate functions whose names can be duplicated in a calling program without incurring error.

A small example illustrating initialization of global and local variables follows:

int cnt=50; // global declaration of cnt as integer; initial value 50
double tmp=100.123; // global declaration of tmp as floating point; initial value 100.123
int function_example()
{
    int x; // local declaration of x as integer; initial value indeterminate 
    double y=tmp; // local declaration of y as floating point; initial value global tmp 
}

To recap, local variables are not initialized until the function containing them is executed. The initial value of global variables are part of the compiled version of the program, so global variables revert to their initialized value whenever the program is run.

Constants

Integer (int) Constants

For most purposes, integer constants are defined using decimal (base 10) integer format. An integer of type int is normally stored in memory as a 32-bit 2's complement integer, which allows values in the range [-231, 231-1]. Integer constants can also be defined using hexadecimal (base 16) integer format (marked by the prefix "0x"), or octal (base 8) integer format (marked by the prefix "0"), although both are awkward for representing negative numbers. For 32-bit two's complement integers, the following are true:

405310 = FD516 = 77258
-110 = FFFFFFFF16 = 377777777778

In C notation, these comparisons become

4053 == 0xFD5 == 07725
-1 == 0xffffffff == 037777777777

which also illustrates each of the three formats. Appending a "u" or "U" to the constant changes its value to the range [0, 232-1] rather than [-231, 231-1]. An "unsigned" specification is sometimes needed when data is to be copied to a variable of a different type; in particular, 8-bit bytes are treated as unsigned integers.

Present day computer memory is normally organized in 8-bit bytes, so hexadecimal pairs are commonly used to exhibit the value of bytes in memory. Earlier computer systems used 3-bit groupings which were represented using octal digits, explaining why octal representation is included as a means for representing integers in C.

The number of bits used for representing integers is system dependent, but is typically 32 bits for type int. The data type long int normally specifies a 64-bit integer, and is designated by appending "L" or "l" to the integer's representation. For most programming needs, integer type (int) constants are sufficient.

Floating Point (double) constants

Fractions are an extension of the integers, and may be represented using a format termed scientific notation (mantissa, exponent); i.e.,

0.<mantissa> × 10<exponent>

In addition to providing means for representing nominal fractional values, this format allows reasonable approximation of astronomically large or infinitesimally small numbers, emphasizing magnitude more so than exactness. Since the decimal point for a number such as 123.4567 is floated to the front in setting the mantissa and exponent (0.1234567×103), the term floating point representation is used.

Computer arithmetic is binary rather than decimal, so the exponent and mantissa are represented internally as base 2 values. Note that the terminating decimal fraction 0.110 when represented as a binary fraction is non-terminating (0.00011001100110011 ... 2). This illustrates that even in simple cases a floating point value may be an approximation, where precision is improved by allowing more bits for the mantissa (double precision doubles the number of bits used for the representation from 32 to 64, adding extra bits for both mantissa and exponent).

A floating point constant can be defined using either ordinary decimal point representation or a form of (base 10) scientific notation; for example,

123.4567 == 12.34567E1 == 1234567e-4 == .1234567e3

While the KRC has floating point operations integrated into its CPU, floating point operations are handled by software for the installed version of Linux to provide backward compatibility, making them significantly slower than their corresponding integer operations (although still fast in human terms). In any event, even hardware floating point operations are slower than their integer counterparts, so it is always a best practice to limit floating point to data that is inherently fractional.

Characters and String Constants

A character constant is given by enclosing the character in single quote marks; e.g., 'K'. Characters are internally encoded in 8-bit bytes using ASCII representation (e.g., the internal ASCII representation of 'K' is the (8-bit) hex pair "4B").

A character string constant is a sequence of characters enclosed in quotation marks, e.g., "This is a character string.". C processes a character string as (1-dimensional) array delimited by the (unprintable) character constant '\0', which marks the end of the string within the array.

The character constant 'K' cannot be used interchangeably with the string constant "K" since 'K' is an 8-bit integer and "K" is a 16-bit string whose first 8-bits are the 8-bits for 'K' and the second 8-bits are for '\0'.

NULL

The special constant NULL (a preprocessor macro included implicitly, or explicitly using #include <stio.h>) is provided by the C preprocessor to represent a NULL pointer. In general, a pointer represents the location (or address) of a data structure in memory. A NULL pointer is one which exists, but which points to nothing.

A pointer that hasn't been initialized has no semantic meaning, in contrast to a pointer initialized to NULL, which points to nothing (think in terms of the empty set used in mathematics, which semantically is a legiimate set, but which has no elements). To check if a pointer variable is pointing to data you compare its value to NULL. As an illustration, suppose a linked list data type is defined to have elements with 2 components, the first of which provides a data value and the second of which is a pointer to the next logical element in the list; e.g., the components for an element in the list might represent a name, and a pointer to the element containing the next name in the list in alphabetical order. If the list is processed to retrieve the name data in alphabetical order, the last element in the list will need to have NULL assigned to its pointer since there are no more names! NULL then provides a (testable) pointer component value that identifies the last element in the list.

Data Types

C supports the following data types among others:

32-bit Integers

32-bit integers are signified by the data type indicator int, by default signed numbers in the (decimal) range [-2,147,483,648 to +2,147,483,647]. If int is prefixed with the "unsigned" directive; for example,

unsigned int x;

the sign bit becomes part of the number and the range becomes [0 to 4,294,967,296]. Newer processors have both 32 bit and 64 bit integers, but 32 bit are typically used at present for backward compatibility.

64-bit Floating Point Numbers

Floating point numbers are best specified by the data type indicator double. 64-bit floating point numbers have at least 15 decimal digits of precision and range from about 10-308 to 10308. The unsigned directive only applies to integers.

8-bit Characters

Characters are 8-bit (unsigned) integers signified by the data type indicator char. A character's value normally represents a standard ASCII character code, most of which are printable. If a variable of type char needs to be treated as an integer, it can be specified as signed char or unsigned char.

Pointers

A C pointer is normally a 32-bit number representing the address of a byte location in memory, even if the CPU supports 64-bit addresses. This remains the case at present to provide backward compatibility for software that compiles for 32-bit address spaces. A pointer that represents the location in memory where information is stored can be used to manipulate the data by performing calculations on the pointer, passing the pointer, or dereferencing the pointer (deference means to obtain the value stored at the location).

Arrays

An array is a data structure used to store a sequence of homogeneous data elements (each element of the array must be of the same data type). Every array has a length which is determined at the time the array is declared. The location of an element in an array is given by supplying its index in brackets. For example, myarray[3] references the fourth element in an array named myarray where indexing starts from 0. By providing the index, data may be stored in or retrieved from the array in the same manner as for other variables. By specifying an array whose elements are (same-sized) arrays, an array with more than one dimension can be defined.

Structures

Structures are used to store non-homogenous but related sets of data. Before a structure can be specified, its struct data type must be defined. Any available data type can be used in its definition, including those that are user defined. In contrast to arrays, elements of a structure are referenced by name instead of number. For example, if a struct Triangle data type is defined as

struct Triangle {
    double sideA;
    double sideB;
    double sideC;
};

then struct Triangle x defines a structure named x of data type struct Triangle. If side A for triangle x is needed, it is referenced by name as x.sideA.

Since a function can return a struct data type, structures provide a way for a function to return multiple data values. Structures can also be useful for reducing the number of arguments passed to functions. More importantly, structures provide a means for creating complex data structure representations such as directed graphs and linked lists. For these kinds of data structures, the struct elements may be dynamically allocated (see malloc) or may be taken from an array set up for this purpose that has the same struct data type.

Enumerated Sets

Enumerated sets are used to associate names with integer values, by default 0,1,2, ... according to how many entries are in the set. For example,

enum letter {F,D,C,B,A};

specifies an enumerated data type named letter with associated names F,D,C,B,A which by default correspond to the values 0,1,2,3,4. The names enumerated can't be the names of variables defined elsewhere since they become variable names of type int.

For a program, the declaration

enum letter grade;

specifies a variable named grade of type enum letter, to which can be assigned any of the names F,D,C,B,A (or equivalently the integer values 0,1,2,3,4). For example,

grade = C; // same as using grade = 2;

Enumerated sets are particularly useful for switch constructions since cases can be referred to by names rather than values; e.g.,

switch(grade) {
   case A:
   	  // case action for an A grade
   	  break;
   case B:
   	  // case action for an B grade
   	  break;
   	  ...
and so forth.

While enumeration names must be distinct, the associated values do not need to be. Note that in most instances, #define could be used in the same manner intended for enumerated sets, but enumerated sets have the advantage of the actual values being generated automatically by default. It also needs to be noted that no check is made to see if a value assigned to an enumeration variable is valid, which means the program needs to ensure the validity. An invalid value may produce an unpredictable outcome; for example, assigning a negative value to the grade variable above may produce an invalid outcome if the variable is being used in a comparison.

Pointers

A pointer is the (numeric) address of the location in memory where a data element is stored. Memory addresses begin at 0 and increase by 1 for each byte of memory. Limited arithmetic operations may be performed on pointers, but the value of the resulting pointer depends on the data type pointed to. For example, adding 1 to the pointer for a char data item increases the pointer value by 1 since that advances the pointer to the next character in memory. In contrast, adding 1 to an int pointer increases its value by 4 since that advances the pointer to the next integer in memory. Using a pointer to try to access memory not allocated to a program will probably cause a system error or crash, so it is important to insure that pointers used in a program address valid objects in memory.

A pointer data type can be defined for any allowed data type, included user-defined data types. When used as a unary operator (one argument), * is the indirection (or "value at address") operator, and is used for defining and dereferencing pointer variables. For example,

int *p;

defines p to be a variable whose value is a pointer to a memory location holding an integer.

When used as a unary operator, & is the memory address operator. The address of a variable named x is given by &x and so if x is an integer variable,

p=&x;

assigns the memory address of variable x to the (integer) pointer variable p.

Retrieving the value pointed to is known as dereferencing the pointer and is given by *p for pointer variable p. For the example above, both variable x and *p represent the same value in memory. *(p+1) retrieves the (integer) value of the next integer in memory (which is 4 bytes further along than x). Furthermore, the contents of the address can be changed by assigning a value to the dereferenced address; e.g.,

*(p+1) = *p + 1;

adds 1 to the integer stored at the address given by p and assigns the result to the address given by p+1.

It is often useful to deal with pointers to objects, but great care must be taken to insure that the pointers used at any point in your code really do point to valid objects in memory. It is easy to get confused by what a pointer is addressing when it is taken from a complex structure with pointers to pointers or something similar.

Pointers are often used instead of global variables to provide a function with access to data external to the function. If a pointer is passed to a function as an argument, the function then has access to the memory location for the pointer, which could also represent a local variable external to the function. If the function uses the pointer to change the value in the memory location, it will also have changed the value of the external local variable.

For C, the scope of function parameters is local to the function, with values assigned to them only when the function is called. Programming languages typically employ one or more evaluation strategies for function parameters. The ones usually cited are call by value, call by reference, and call by name.

Arrays

Arrays can be defined in C for any supported data type, including user defined data types. The name of an array is actually a pointer to its first element, the one whose index is 0, so passing an array to a function is a call by reference. Multi-dimensional arrays are defined as arrays of arrays (or arrays of pointers). Arrays are useful for allocating space for many instances of a given data type, arranged sequentially in memory, which provides means for iterating over the set of values in the array.

Declaring and Initializing Arrays

The definition for an array specifies its data type, name, and index structure, which is given inside square brackets. The following statement declares an array of ten integers:

int ex_array[10];

The elements of the array are numbered from 0 to 9. Elements are accessed by enclosing the index number within square brackets; e.g., ex_array[4] denotes the fifth element of the array ex_array (since counting begins at zero). Note that ex_array == &ex_array[0].

Arrays not initialized at declaration contain indeterminate values. Arrays may be fully or partially initialized at declaration by specifying the array elements within curly braces, separating the array elements from each other by commas. For example,

int ex_array[10]= {3, 4, 5, -8, 17, 301};

initializes the first six elements of ex_array, with ex_array[4] equaling 17, ex_array[0] equaling 3, etc.

If no size value is specified within the square brackets when the array is declared, but initialization information is given, then the size of the array is determined by the number of initialization elements given in the declaration. If a size is specified and initialization data is given, but the length of the initialization data exceeds the specified length for the array, the excess data will be ignored (and the compiler will issue a warning).

Character Strings

Character strings are implemented as arrays of characters. An array of characters can be initialized character by character, but can also be initialized by using a string constant, for example:

char ex_string[]= "Hello there";

This initializes the character array ex_string with the ASCII values of the characters in "Hello there" terminated by the (unprintable) ASCII character '\0'. The length of the array is 12, which is the number of characters in "Hello there" plus 1 for the '\0' string termination character. If the array length had been declared, the initialization would leave the balance of the array indeterminate if more than 12 and truncated (including '\0') if less than 12. When a character array containing a string constant is used as an argument for printf, the format specifier "%s" marks where to insert the string in the formatted output; for example,

char ans[10]="no";
int q=3;
printf("The answer to %d is %s.\n",q,ans);

produces the (formatted) output "The answer to 3 is no.".

The standard C library has string functions for assigning strings (strcpy), determining string length (strlen), combining strings (strcat), and the like (look for string functions in your C reference). For example,

strcpy(s,"Example string");

is used to copy the character string in the second argument of strcpy to the character array in the first.

To make sure that the C preprocessor includes the prototype declarations for the string functions, programs that use them normally have the C preprocessor directive

#include <string.h>

at the start of the program code. This directive, like the #include <stdio.h> directive is ignored if it is duplicated elsewhere in the code.

When a character array is initialized character by character using the curly braces syntax, unless '\0' is included, there is no string delimiter, and printing the array as a character string using printf will produce indeterminate results. When declaring a character array that is intended to hold character strings, the array size needs to be at least 1 larger than the size of the maximum string it will be used for to allow for the '\0' string termination character. For example, given

char pg_string[81];

strings of length up to 80 can be stored in the variable pg_string

Passing Arrays as Arguments

When an array is passed to a function as an argument, the address of the array's initial element is actually passed, rather than the elements of the array, a call by reference as discussed earlier. Hence there is not a local copy of the array inside the function, and any modifications the function makes to the array are to its location in memory when declared (what is local to the function is the copy of the address of the array's initial element passed as an argument).

For a function to be able to treat an argument as an array, the corresponding parameter specification needs to provide both the the data type of the array and its dimensional structure.

As an example, the following function has arguments for an index and an array, printing the array element at the index value.

void print_element(int indx, int arr[])
{
    printf("Value at index %d is %d\n", indx, arr[indx]);
}

The use of (empty) square brackets with the parameter in this example (int arr[]) specifies that the parameter is 1-dimensional array. While it is sometime necessary to use pointer to access array elements it is usually the best practice to use bracket notation (e.g., arr[indx]) rather than pointers (e.g., *(arr + indx)).

Either of the following two example calls to the function for an array of integers ex_array of suitable length will work, whichever of the two versions is used:

print_element(3, ex_array);
print_element(4, &ex_array[0]);

Multi-dimensional Arrays

A two-dimensional array is just one-dimensional array whose elements are one-dimensional arrays. For example

int k[2][3];

specifies an array of length 2 whose objects are length 3 arrays integers. k can be viewed as a two-dimensional array with 2 rows and 3 columns, where the first row has as elements k[0][0], k[0][1], k[0][2] and the second the elements k[1][0], k[1][1], k[1][2]. k[0] and k[1] represent rows of 3 elements each. Hence, in addition to accessing any element of the array using index bracket notation, any row in the array can be accessed similarly. Arrays with any number of dimensions can be generalized from this example by adding more brackets in the array declaration.

For multi-dimensional arrays the array name no longer treated as equivalent to a pointer, although passing the array as a paremeter is sill the equivalent of passing a pointer. Since a multi-dimensional array is an array of arrays, to be passed to a function as an argument, the parameter specification must provide the size of the array components; e.g., int x[][4] specifies a parameter for an argument that is an array of length 4 arrays of integers. For the 2-dimensional array k given above, &k[0][0] is the address where the array structure begins, and &k[0][0]+1 is &k[0][1].

The initialization

int k[2][3] = {{0,1},{3,4,5}};

illustrates initialization of parts of the array, where only k[0][2] has not been initialized. Conceptually the array is

0 1 ?
3 4 5

Structures

As noted above, structures are used to store non-homogenous but related sets of data. In order to specify a structure, a struct data type for it must first be defined. Any available data type can be used in the structure definition, including those that are user defined. The elements of a structure are referenced by name to access them.

Since a function can return a struct data type, structures also provide a way for a function to return multiple (named) data values. However, the primary purpose of structures is to provide a means for creating complex data structure representations such as directed graphs and linked lists. In the construction of this kind of data structure, structure elements may be dynamically allocated (see malloc) or alternatively, be taken from an array having the struct data type.

The following example illustrates structure definition, creation of an array of structures, and access to its elements.

#include <stdio.h> // make sure the I/O and string function declarations are present
#include <string.h>
typedef struct ex_stype { // structure definition for "folder" data type  
    int i;           // component is an integer
    char name[81];   // component is a string
} folder;
folder record[10]; // "record" is a data structure that is a global array of up to 10 folders
void assign(folder *x, char nm[], int i); // function to assign a name and number for a record
void show(folder x); // function to display the number and name stored in a record

int main()
{
    folder record[10]; // "record" is a data structure that is anarray of up to 10 folders
    assign(&record[0],"one",1); // assign name and number for record[0]
    assign(&record[1],"two",2); // assign name and number for record[1]
    show(record[0]);            // display name and number for record[0]
    show(record[1]);            // display name and number for record[1]
    return 0;
}
void assign(folder *x, char nm[], int i)
{
    (*x).i=i;               // set i as the number component of structure x
    strcpy ((*x).name, nm); // use the string library function strcpy to set nm as the name for x
}
void show(folder x)
{
    printf("name %d is %s\n",x.i,x.name);
}

The first part of the example is the specification of the struct data type used to define the data structure ds used in the program. typedef is used to tag "struct ex_stype" with the simpler type name "folder". A more complex data structure is formed as an array "record", each component of which is of type "folder" (each "folder" has a number and name).

The two functions, "assign" and "show", illustrate using the dot operator (.) to assign values to the components of a structure and to access the respective components of a structure. Since the function "assign" needs to change values in the array "record", which is local to main, a call by reference is necessary, explaining why the address of a folder is needed, allowing the assign function to modify folders external to it.

Pointers to struct data types can also be used, just like pointers to any other type. When a pointer is for a struct data type, an "arrow" (->) notation can be used to access elements of the structure addressed by the pointer; for example,

    struct ex_stype *sptr;
    sptr = &record[9];
    sptr->i = 9;
    strcpy(sptr->s, "record 9");

The dot operator can be equivalently used, but has the clumsier notation (*sptr).i as opposed to sptr->i.

Just as for arrays, only pointers to structures, not the structures themselves, can be passed to or returned from functions.

Initialization Examples

Complex data structures formed as arrays or as structures may be initialized upon declaration with a sequence of constant values contained within curly braces and separated by commas.

Character arrays may also be initialized as a string from a string of characters enclosed in (double) quote marks.

In initialization of a one dimensional array, the length (inside the square brackets) can be left blank, in which case the allocated length is determined by what is assigned to the array. The declaration for a multi-dimensional array must specify the size of all dimensions after the first dimension. If a length is specified, and initialization data overflows that length, a warning is issued and the excess data is ignored. If the initialization data falls short of the specified size the rest will be indeterminate.

The following example illustrates a variety of different initializations:

/* declare global variables of various types */
int i = 50; // single basic variable 
int *ptr = NULL; // single basic pointer variable 
int x=1, y, z=3; // multiple basic variables 
double farr[3]= {1.2, 3.6, 7.4}; // one dimensional array 
int iarr[5]= {1, 3, 7}; // one dimensional array, last 2 indeterminate 
int jarr[]= {2, 4, 6, 8}; // one dimensional array, derived length 4 
char carr[2][3]={{'a', 'b', 'c'}, {'b', 'd', 'f'}}; // two dimensional array 
int xarr[2][5]={{1, 2, 3}, {2, 4, 6}}; // two dimensional array, last 3 rows indeterminate 
int yarr[][2]={{1, 2}, {2, 4}, {3,6}}; // two dimensional array, derived size 3x2 
char c[]="A B C"; // string, array size 6=5+1 
char sarr[5][10]={"a b c","d","e f"}; // array of strings, first 6 characters of row 1 initialized, first 2 of row 2, first 4 of row 3, last 2 rows indeterminate 
struct employee {
    char name[31];
    double weight;
    struct employee *nextrec;  // pointer (self-referential) to the struct for another employee 
};
struct employee a_node = {"John Doe", 165.4, NULL}; // perhaps a linked list node 
struct employee elist[2] =
    {{"JFK", 163.1, NULL }, {"LBJ"}}; // not everything has to be initialized 
// example function which declares local variables of various types, initialized from global variables 
int f_locals()
{
    int x = i; // local x, global i (value 50) 
    int y = yarr[2][1]; // local y, global yarr (value 6) 
    int *iptr=&i; // local iptr, address of global i 
    struct employee wx={"Jane Doe", 115.2, &a_node}; // local struct wx, pointed to global a_node 
        . . .
}

Statements and Expressions

C has unary operations (one argument), binary operations (2 arguments), and even a ternary operation (3 arguments) for performing actions upon data used in a program. Expressions are combinations using one or more operations. Statements incorporate expressions, assignments, function calls, and control flow constructions to form blocks of code for a C program and are terminated by a semi-colon (;).

Operators

Each of the data types has a set of operators for operations that may be performed on expressions for that data type (e.g., x + y is an expression).

Integer Operators

The following operators are provided for integer data:

In C, an integer used in a Boolean expression represents FALSE if it is 0 and TRUE if it is non-zero. In determining the result of the logical operation x && y, if x is FALSE, the value returned by the operation is FALSE without checking y since logically this is enough to determine the result has to be FALSE. Similarly, for x || y, if x is TRUE, the value returned by the operation is TRUE without checking y since logically that is sufficient for determining the result is TRUE. In all other cases, the value for both operands is checked. This can be useful if x is being used as a TRUE/FALSE flag where y may initially be invalid when x is FALSE.

Integer promotion is the action of "promoting" an arithmetic operand (such as short or char) to a 32 bit integer, similar to type casting or coercion. Unlike its counterpart, unary -, it is seldom used since promotion of operands is automatic in any arithmetic expression.

Floating Point Operators

The following subset of integer operators are provided for floating point data:

As noted earlier, for the KRC, floating point operations are implemented in software for backward compatibility, which means they are significantly more resource intensive than integer operations. For this reason, floating point values should not be substituted for integers in iterative processes (such as a loop counter).

There is a large selection of math functions in the C library for performing calculations that are inherently floating point. For example,

double a, x, y;
x = sqrt(2); // x is assigned the square of 2
x = log(5);  // x is assigned the base e logarithm of 5
x = log2(8); // x is assigned the base 2 logarithm of 5
x = exp(3);  // x is assigned e cubed
x = exp2(3); // x is assigned 2 cubed
y = pow(x,0.333333); // y is assigned the (approximate) cube root of x
x = sin(3);  // x is assigned the sine of 3 (radians)
a = asin(0.123); // a is assigned the primary angle (in radians) having sine 0.123

There are many more functions in each of these categories, plus functions for other kinds of manipulation of floating point data. The section below for KIPR Library Functions includes many of these under the "Math" category designation.

Characters

For C, a character is an 8-bit unsigned integer whose value is one of the 256 ASCII character codes (ASCII is the acronym for American Standard Code for Information Interchange). Integer operations can be performed on character data since if a character variable is used in an integer operation, it is automatically coerced from an 8-bit unsigned integer into a (positive) 32-bit integer for the computation. ASCII encodes 0 ... 9 in order, then later A ... Z in order, and then later a ... z in order, to enable easy alphabetizing using integer comparison. Adding 32 to an upper case character will convert it to lower case.

When a value is stored into a character variable, it is coerced into an 8-bit character (by truncating the upper bits). Since character data occurs in 8-bit bytes, character string data is stored in consecutive byte locations in memory; i.e., treating memory as character data provides a means to address and step through memory one byte at a time.

Compound Assignment Operators

The basic assignment operator is =. The following statement adds 2 to the value of a.

a = a + 2;

The compound assignment operator += does exactly the same thing; i.e.,

a += 2;

All of the following binary operators can be used in a compound assignment:

+,  -,  *,  /,  %,  <<,  >>,  &,  ^,  |

Unary Increment and Decrement

The increment operator ++ increments its operand by 1. When used prefix (++x) it has a different semantic interpretation then when used postfix (x++). The simple cases

x++;
++x;
x = x + 1;
x+=1;

are all equivalent.

When used in a more complex expression, the value of ++x and x change before the result is used in the expression. For x++, the value of x++ and x change after the result is used in the expression. For example, for the following constructions using ++x and x++

x = 3; printf("%d  %d\n", x, ++x);
x = 3; printf("%d  %d\n", ++x, x);
x = 3; printf("%d  %d\n", x, x++);
x = 3; printf("%d  %d\n", x++, x);
x = 3; printf("%d  %d\n", x, x+1);

the displayed text will be

4 4
4 4
4 3
3 3
3 4

Confusing? To understand this you also have to know the arguments of are evaluated from right to left in setting values passed. Here's a (long-winded) explanation for how the outcome above is produced by each program line:

Line 1: The ++x argument is cleared before values are passed to printf and by incrementing beforehand both argument values are set to 4.
Line 2: Just as for Line 1, the ++x argument is cleared before values are passed to printf.
Line 3: The x++ argument is rightmost and gets the current value 3 of x because the increment occurs afterwards. The new value of x (4) then becomes the value used for the x argument.
Line 4: The x argument is rightmost and has value 3. The x++ argument sets the value 3 before it increments. The value of x subsequent to the printf is 4.
Line 5: This is the most intuitive case, because it doesn't use ++, but notice it also doesn't increment x.
The difficulty for explaining what appears at first glance to be straight forward code illustrates why the use of these operators in normally limited to the simpler cases.

The interpretation for a combination such as x+++y is dependent on the compiler being used so such variations are avoided, although not prohibited. [So which is it? (x++)+y or x+(++y)? The answer could be neither!]

The decrement operator -- decrements its operation by 1. Its semantics mirror those of ++.

Data Access Operators

Data access operators are ones used to manipulate components of arrays and data structures, or to directly manipulate data via memory addresses.

Precedence and Order of Evaluation

As per Kernighan and Ritchie's book, the following table summarizes the rules for precedence and associativity for the C operators. Operators listed earlier in the table have higher precedence; operators on the same line of the table have equal precedence and when used without parentheses are cleared left to right.

Operator   Associativity 
  ()  []  ->  . left to right
  !  ~  ++  --  -  *  &  (<data-type>)  sizeof   right to left
  *  /  % left to right
  +  - left to right
  <<  >> left to right
  <  <=  >  >= left to right
  ==  != left to right
  & left to right
  ^ left to right
  | left to right
  && left to right
  || left to right
  ?: right to left
  =  +=  -=  *=  /=  %=  &=  ^=  |=  <<=  >>=   right to left
  , left to right
Unary +, -, and * have higher precedence than their binary forms.

Control Flow

The order in which a computer executes machine-level operations stored in memory is normally sequential, with the exception of those operations which redirect the flow of execution to continue elsewhere in computer memory. This capability is reflected in C programs by control flow statements which serve to redirect the sequential step by step flow the program would follow otherwise. There are two classes of control flow commands, selection and iteration. Selection commands use a condition test to determine what statement or block of statements to execute next (if-else, switch, and the ternary operator ?:). Iteration commands use a condition test to determine how many times to repeat the execution of a statement or block of statements (while, for, do-while). C also provides means for exiting a control flow program structure independent of the condition test (break) and to skip the rest of a block, continuing to the next iteration (continue).

Statements and Blocks

Each C statement is terminated by a semicolon. A program block is a sequence of statements grouped together inside curly braces. Variables may be defined inside a block and are local to the block (their scope does not extend outside of the block). The object for a control flow statement may be a single statement or a block of statements, except for ?:, which as an operator applies to expressions rather than statements.

If-Else

The if statement is a selection statement for making yes/no and either/or decisions, For an either/or decision, the if statement is paired with an else statement (which can only be used in a pairing with if).

The syntax for if is

if (<expression>)
    <statement or block>

When paired with else the syntax is

if (<expression>)
    <statement or block>
else
    <statement or block>

When the <expression> evaluates as TRUE (i.e., is not equal to zero), then the <statement or block> that follows if is executed.

When the <expression> evaluates as FALSE (i.e., is equal to 0), then the <statement or block> that follows if is not executed and if there is an else paired with if, then the <statement or block>; that follows else is executed.

In effect, these constructions use syntax that corresponds to how similar logic is expressed in English, such as

Ternary Operator ?:

The ternary operator ?: provides a compact means for expressing if-else logic where assignment statements are employed as the if-else actions. For example, the following if-else construction assigns the larger of variables x and y to variable z:

if (x > y)
    z = x;
else
    z = y;

This same construction can be implemented by using the ternary operator ?:

z = (x > y) ? x : y;

The ternary expression is formed from a condition test (x < y) followed by a question mark (?), then the expression (x) to assign to z if the condition evaluates as TRUE, separated by a colon (:) and then the expression (y) to assign to z if the condition evaluates as FALSE. Strictly speaking, given the low precedence level of the operator, the parentheses around the condition could be omitted, but are advisable to facilitate identifying the operator's three operands.

ANSI C requires that only the expression whose value is to be assigned to z be evaluated, just as in the if-else equivalent. The ternary operator is frequently used for obtaining absolute value; e.g.,

absx = (x < 0) ? -x : x;

While

The while statement in C is the iteration statement most commonly used for managing program loops. A loop is a sequence of program logic that is repeated multiple times (0 or more); for example,, a loop might be used to print out the elapsed time second by second while waiting for a button to be pressed.

The syntax for a while statement loop is

while (<expression>)
    <statement or block>

When the <expression> evaluates as TRUE (i.e., is not equal to zero), then the <statement or block> that follows while is executed, after which the process repeats, starting with evaluation of the <expression> again.

When the <expression> evaluates as FALSE (i.e., is equal to 0), then the <statement or block> that follows while is not executed and the program moves on to the statement following the <statement or block> for the while.

Note that the loop continues until something happens that causes the <expression> to evaluate as FALSE (e.g., a button is pressed or a counting variable has exceeded its limit). It is up to the programmer to ensure that within the body of the loop (or otherwise) the condition will eventually be FALSE (otherwise the loop will continue until the program is halted by external intervention).

A loop that doesn't terminate is termed an infinite loop. An infinite loop is one that will continue its iteration until the program is halted. An indefinite loop is one whose iteration will continue until some external action occurs (such as a button press). It is easy to create an infinite loop using while. For example,

while(1);

forms an infinite loop because the <expression> for the while is 1 which is always TRUE, and the <statement or block> is empty (;), providing no means other than a forced program halt to end the ongoing loop iteration (which is repeatedly doing nothing).

A common error to avoid when programming a while statement loop is exemplified by the following:

while (i < 10); // misplaced semi-colon
    x[i++] = i;

In this case, the misplaced semi-colon for the while has made its <statement or block> component empty. Assuming i is less than 10 when the loop starts, this semantic error makes the loop infinite and the program will "hang" at this point, requiring a forced halt to end it. For ideas on how to locate this kind of semantic error for a program, see the section below on program debugging.

Break

The break statement in C provides a means for a program to break out of executing statements within a loop structure or a multi-way selection to proceed with execution of the program statements that come next. A condition test for break is sometimes used within what would otherwise be an infinite loop to break out of it. The break statement applies to while, for, do-while, and switch.

For example,

int i; // counter will need some initial value
while (i < 100) { // has the counter reached its limit?
    printf("cnt = %d\n", i++); // show the counter and increment it
    if (side_button() != 0) break; // exit if the side_button is pressed
    msleep(1000); // pause for one second
}

uses a break to exit the while loop.

Continue

The continue statement in C provides a means for a program in a while, for, or do-while loop to skip the balance of the current iteration, continuing to the next iteration if the loop condition test doesn't end the loop.

For example,

int i; // counter will need some initial value
while (i < 100) { // has the counter reached its limit?
    if (i <= 0) { // don't start cnt until i reaches 1
       i+=1;
       continue;
    }
    printf("cnt = %d\n", i++); // show the counter and increment it
    if (side_button() != 0) break; // exit if the side_button is pressed
    msleep(1000); // pause for one second
}

Return

The return statement in C both ends a function and provides a means for the function to return a value to the calling function. A function also ends once its terminating brace is reached or if a return statement having no return value is executed. The calling function can ignore a returned value in any case.

For

The for statement in C is normally used for managing loops that employ an iteration counter. A while statement loop could also be used for this purpose, but in many cases the program logic is clearer if a for statement is employed to control the loop.

The syntax for a for statement loop is

for (<expr-1>;<expr-2>;<expr-3>)
    <statement or block>

The behavior of the for statement loop is equivalent to that of the following while statement loop:

<expr-1>; // initialize the counter (prime the pump)
while (<expr-2>) { // has the counter reached its limit? (have we pumped enough in?)
    <statement or block>
    <expr-3>; // increment the counter (pump some more in)
}

For example, the following code counts assigns values from 0 to 99 to an integer array

int i, arr[100];
for (i = 0; i < 100; i++)
    arr[i] = i;

Switch-Case

The switch selection statement for C is used to select one of a series of case targets. The case targets for a switch statement are grouped together inside curly braces as a program block. Each case target is followed by a series of C statements which represent that case. The switch selection in effect skips past cases until it reaches the one selected, and program execution continues from there. If a break statement is in the statements following a case target, then the program exits the switch statement's block at that point, in effect skipping any remaining cases.

if else selection is used to select one of two cases. switch selection is used to select 0 or more cases.

The syntax for a switch statement is as follows:

switch (<expression>) { // switch to the case target whose <constant> matches the value of the <expression> 
case <constant>:
    <0 or more C statements>
    break; // optional - otherwise, program execution continues with the next case 

<additional case targets>

default: // optional target used when selection didn't match any case target 
    <0 or more C statements>
    break; // optional - otherwise, program execution continues without finding a case 
}

The first case target whose <constant> matches the value of the expression for the switch statement is the one selected. Program execution continues from the selected case target until either a break statement is encountered, or the end of the switch program block is reached. If there is no case match, then the default target is selected. Absent a default target and a case match, the switch program block is skipped. Putting in a default case with a break statement is considered to be good form, and recognizes the likelihood of more cases being added under program maintenance.

Normally, each case is ended with a break statement since the primary purpose of the switch statement is to make the choice of one of several possible cases to execute; for example,

char answer[81];
switch(answer[0])
{
    case 'y':
        printf("answer was yes\n");
        break;
    case 'n':
        printf("answer was no\n");
        break;
    default:
        printf("answer was not understood\n");
}

If user input "yes sir" is captured in the character string answer, then the text "answer was yes" will be printed to the screen. If if was "no sir", then the "answer was no" would have been printed, and if it was "No sir", the text "answer was not understood" would have been printed (since C is case sensitive, 'n' ≠ 'N').

Do-While

The do-while statement in C is less commonly used for controlling loop iteration than the while and for statements.

The syntax of a do-while statement is as follows:

do
    <statement or block>
while (<expression>);

In contrast to the while and for statements, the body of the loop for a do-while statement will be executed at least once since the <expression> controlling loop iteration is evaluated at the end of the loop rather than at its beginning. Sometimes program logic for a loop is best expressed by having the condition test occur at the bottom of the loop. For example,

do
    printf(".");
while (seconds() < 5);

The logic insures that "." will be printed at least once (so something will always be printed), in contrast to

while (seconds() < 5)
    printf(".");

for which nothing will be printed if the value returned by the function seconds already exceeds 5.

Programming Style

Many authors have expressed opinions on programming style, which for C generally consists of how key elements of a program are exhibited, how comments are handled, and how white space is employed, most notably in how block structures are offset by placement and indentation. The manner in which a program is written affects its readability, ease of understanding, and effort for debugging, enhancement, and maintenance.

The most commonly used style, K&R Style, reflects how programs are presented in Kernighan and Ritchie's book on C The C Programming Language (2nd Edition). This style, or a variant, is advocated by most C programming experts and is the style observed in this manual.

White Space

As noted earlier, the term "white space" references combinations of characters that when printed produce a blank area on the display. For C, white space is formed by using characters such as spaces, new lines, and tabs. The white space characters in C are

  Character    Print Action 
  ' ' space
  '\n' new line
  '\t' horizontal tab
  '\v' vertical tab
  '\r' carriage return
  '\f' formfeed

The print result may differ from one display device to another (for example, while '\n' has the same outcome for almost every device, '\r' may behave the same as '\n' for some devices but on others simply reposition printing at the start of the current line).

The C compiler either ignores white space that is not imbedded inside double quote marks, or collapses it to a single generic white space character for compilation purposes. This means that other than for character strings, white space can be used for things like indentation that improve program readability. White space is required where it is needed to separate adjacent objects, but if another separator such as a parenthesis, comma, curly brace, square bracket, semi-colon, etc is present, white space is not required.

Indentation

Good use of indentation offsets program blocks, not only making a program more easily understood, but making it easier to determine if the curly braces are matched and block structures will produce the desired semantic results. From the C compiler's point of view,

/* Simple example: C Programmer's Manual */
int main()
{
    printf("Hello, world!\n"); /* simple example */
}

and

/* Simple example: C Programmer's Manual */int main(){printf("Hello, world!\n");} /* simple example */

are equivalent, but it is pretty clear the use of white space to set off the elements of the program in the first version makes it more understandable than for the second. This distinction becomes even more important when nested selection and iteration statements are being employed. For an admittedly extreme example, the following construction as presented will compile, but from a human readability point of view is almost incomprehensible:

switch(i){case 0:while(j++<100){k++;if(k>10)break;}break;case 1:if(j>50)k--;else while(j-->=0)k++;break;}

In addition to better presenting overall program logic, use of a consistent programming style facilitates debugging programs. Just as writers develop styles intended to make the text they produce more easily read and understood, programmers develop styles for making their programs easier to read and understand, but with the added objective of making them easier to debug. The KISS IDE program editor provides facilities for automatic indentation that reflects common practice for C programmers.

Commenting

Comments are the means for programmers to integrate documentation into a program. The lead comment typically specifies the purpose of the program. For large projects, it is a common practice to also include a (bracketed) comment on program history, in particular reflecting the changes that have been made to the program, including by whom, when, and where in the program.

Multi-line comments are typically used to offset description and purpose of major program components. Single-line comments are useful for provide an explanation of logic or reasoning that might be subsequently useful if that part of the program needs to be revisited for debugging or other purposes.

Program Debugging

Debugging is the process of correcting syntactic and semantic errors found in a program.

Syntax Errors

The C compiler cannot compile a program that has syntax errors such as a missing semi-colon or other program construction error. Syntax errors are the ones most easily corrected since the compiler can identify both where they occur and what the problem is. The KISS IDE lists any syntax errors found during program compilation in an error panel below the program code. The interface provides the line number and position within the line identifying the location of the error, with a description of the cause of the error. Since the mistake causing the first error listed will typically cause additional syntax errors within the program, it is often the case that correcting the syntax causing the first error will fix the rest.

Semantic Errors

Just because a program compiles does not mean it will do what is intended, which is termed program semantics. A semantic error manifests itself during program execution, either because the program crashes or it produces erroneous results. Semantic errors are caused by errors in program logic or programmer oversights (such as failure to initialize a variable). They can be difficult to track down because it usually is not clear where in program execution the error occurred.

For a program which relies on data inputs, determining the presence of semantic errors requires testing the program for a representative set of possible data inputs, including so-called boundary conditions. For example, if a data input has a range from 0 to 1000, then 0 and 1000 are the boundaries for the data, and one of these, 0, will cause the program to crash if used in the denominator of a division operation. That means that once the part of the program where the error occurs has been located, program logic has to be adjusted to check the data input to see if it is 0 before using it in the division. For complex systems, program testing is an ongoing part of the software development cycle.

Semantic errors that are rare events are difficult to correct, since being able to repeat what causes the error is key to being able to resolve it. Once a semantic error can be reliably repeated, various techniques are employed to determine the cause and to locate where in the program it occurred. Only the basic techniques will be covered here, but operating systems provide debugging tools designed to determine the cause of an error and to facilitate locating where in the program code it occurred.

Using printf and Commenting for Diagnostic Purposes

For the smaller C programs constructed for environments like the KRC, most semantic errors that occur can be corrected quickly using one or both of the following two approaches.

For larger programs, a system debugging tool may be called for. Unix systems include the debugging tool sdb, which has its own command structure that has to be learned to be used effectively.

Display Screen Printing

The C function printf is used for printing to the KRC display screen, where print output is wrapped to subsequent lines if too long for the display, and is scrolled upward as the capacity of the screen is exceeded. For controlled printing to the display, the KIPR Library additionally includes the display_printf version of printf, which prints starting at a specified (column, row) position on the KRC display and which doesn't wrap text which exceeds screen width. The number of rows available for controlled printing is different for the 3 button (A,B,C) version of the KRC program console screen than for the 6 button (X,Y,Z,A,B,C) version.

The syntax of printf is the following:

printf(<format-string>, ... );

The <format-string> is a character string which includes 0 or more "%" codes. For each % code, a corresponding argument is supplied to printf after the <format-string> to provide the value to be formatted and inserted into the print output in place of its % code.

It is important to note that in resolving % codes printf ignores the data type of the corresponding argument. In other words, since printf does not require a data type match between an argument and its corresponding % code, the argument will not be automatically coerced as it would be for an assignment statement. This is particularly important to keep in mind when both integer and floating point values are being used.

The use of % code formatting is best illustrated by some examples.

Printing Examples

Example 1: Printing a message

A printf statement employing no % codes simply prints out the <format-string> as a message; for example,

printf("Hello, world!\n");

The character \n at the end of the string signifies an advance to the next (or new) line for any further printing by the program. When the bottom of the display is reached, the display is scrolled (up) for each line subsequently printed.

Example 2: Printing an integer

In most environments, integers in C are stored in memory as 32-bit 2's complement integers. The % code used to format the corresponding argument as a ± decimal integer is "%d" (or "%i"). For the following example, the value of the variable x is displayed as a decimal integer, with a leading minus sign if the integer is negative:

printf("Value is %d\n", x);

The code "%d" specifies that the first argument after the initial character string in the argument list for printf (the variable x) is to be formatted as a ± decimal integer and inserted in place of "%d" in the printed output. The length of the formatted output will vary depending on the number.

Example 3: Other integer print formats

As already seen, the "%d" code is used to format an argument in ± decimal integer form. The "%x" and "%X" codes are used to format an argument in (32 bit) 2's complement form using hexadecimal (hex) digits 0 ..9,A,B,C,D,E,F (each of which represents 4 bits in binary, 0000, 0001, 0010, ..., 1111). Negative 2's complement numbers have a leading 1, so negative 32-bit integers written in hex require 8 hex digits beginning with one of the hex digits 8-F; e.g., -28 when formatted using "%X" is FFFFFFE4. For positive numbers, the space required will vary unless a % code modifier for length is employed; e.g., using the % code "%8X" for the integer 28 will yield "      1C" (8 spaces are consumed).

It needs to be emphasized that a length modifier represents the minimum amount of space that will be used when the formatted result is inserted in the print line. If there isn't enough space, printf will use more.

If the code "%08X" is used instead of "%8X", leading 0's will replace any leading spaces (so using %08X" for the integer 28 will yield "0000001C" as the formatted result). This applies to "%d" as well; e.g., formatting the numbers -28 and 28 using "%04d" yields as formatted results "-028" and "0028", respectively. A length specifier is employed when numbers are being printed to line up uniformly in columns. If you always want the sign of the number printed, not just when it is negative, the code "%+d" forces the sign to be printed as + or -.

For example, the printf statement

printf("Values are %d, %X, %04x\n", -28, -28, 28);

displays the text string

Values are -28, FFFFFFE4, 001c

The third % code in this printf ("%04x") has a length specifier (plus a leading 0's specifier) and lower case is used for the hex digits. If the value to be formatted requires more space, printf will override the length specifier. It is up to the programmer when using a length specifier to anticipate number size and make the length specifier sufficiently large.

Examples 2 and 3 are representative of output formats for integers using printf. There are additional integer output formats (including ones for representing integers using octal digits and for unsigned integers) described in most C references.

Example 4: Printing a floating point number

The % code used to format the corresponding argument as a floating point number is "%f". In formatting for floating point, printf rounds the fractional part of the number up according to the number of decimal places used (its precision). The default precision is 6 decimal places. A precision specifier is used to limit this. For example, "%.2f" limits the precision to 2 decimal places. For example,

printf("Values are %f, %.2f\n", 1.266, -1.266);

displays the text string

Values are 1.266000, -1.27

The code "%f" specifies that the first argument after the initial character string in the argument list for printf (1.266) is to be formatted as a floating point number rounded to 6 decimal places and inserted in place of "%f" in the printed output (since 6 decimal places is sufficient, no rounding occurs). In contrast, the code "%.2f" for the second argument rounds its number (-1.266) to 2 decimal places to yield -1.27.

There are additional floating point % codes described in C references that are used for formatting very large (or very small) floating point numbers in scientific (exponential) notation (±<mantissa> E ±<exponent>).

Example 5: Printing character strings

The % code "%c" is used to format character data. The % code "%s" is used for character strings, since they are frequently needed for print display.

char header[] = "Data: ", cs='a';
int x=28;
printf("%s%c = %d\n", header, cs, x);

displays the text string

Data: a = 28

where for the printed output "%s" is replaced by the character string at the memory location corresponding to header, "%c" is replaced by the character given by cs, and "%d" is replaced by the ± decimal representation of the data given by x.

Example 6: Printing using display_printf

The function display_printf is only valid for the KRC. It is like the standard printf function except its first two arguments specify the column and row (zero indexed) on the display where printing starts. The remaining arguments are the same as for printf. The following example prints

2. KIPR store

starting at column 5 on the 3rd row of the display. Note that the first row/first column of the display has column index 0 and row index 0.

int i = 2;
char nm[] = "KIPR store";
display_printf(4,2,"%d. %s ",i,nm);

The column range for display_printf is 0 - 41. The row range for the 3 button (A,B,C) case is 0 - 9 and for the 6 button (X,Y,Z,A,B,C) case is 0 - 7.

display_printf does not wrap, truncating strings that go beyond the end of a row on the display.

The special character '\n' should be avoided when using display_printf since it will have unpredictable effects on what is displayed.

When repeatedly printing variable data to the same place on the display, care must be taken to add enough spaces to what is printed to clear artifacts from the previous print; e.g., if one of the following was used,

display_printf(4,2,"%d. %s",i,nm); // NO
display_printf(4,2,"%d. %s ",i,nm); // YES

then if we had already printed "2. KIPR store" and changed nm to "Staffing" then the first of these would print

2. Staffingre

whereas the second would add enough spaces on the end to clear the artifacts, printing

2. Staffing

printf % Codes Summary

 % Code     Associated Data Type  Format Outcome
%d or %i int ± decimal integer
%x or %X int   2's complement hexadecimal representation 
%f double   ± number with decimal point 
%c char   ASCII character (low byte for int  )
%s char *   ASCII characters until '\0' is reached 

There are additional % codes for printf and more complex % code modifiers that can be used in printf statements. For information on these, see a standard C reference.

C Preprocessor

Before the C compiler receives a file, it is first passed through the C preprocessor to prepare the file for compilation. The preprocessor clears away comments, shrinks white space to single characters, and processes any preprocessor directives present in the program.

Preprocessor Directives

Preprocessor directives are identified by the "#" symbol, which must be the first entry on a line for the directive to be recognized by the C preprocessor.

The two primary preprocessor directive are #include for inserting files into the program code and #define for defining macros that are expanded wherever their names appear in program code.

For a macro call to be recognized by the preprocessor, it's definition has to occur at some point earlier in the program. Macro definitions begin with the macro directive #define and are usually grouped together at the beginning of the file in which they appear. The preprocessor will flag a macro definition as being a duplicate if its name is reused for another #define in the same file. If more than one file is employed for a program, a preprocessor directive applies only within the file where it is defined.

Since the programmer may or may not know what #define commands appear in a #include file, commands are provided to check to see if a macro has already been defined (#ifdef .. #endif and #ifndef .. #endif). For example,

#ifndef PI
   #define PI 3.1416159
#endif

When the preprocessor encounters a valid macro name in the program code, the macro is called and its name "expanded" to be replaced by whatever text the macro generates; e.g., when the macro name PI is encountered by the preprocessor it replaces it with the text 3.1416159 (for the C compiler to subsequently interpret as a floating point constant).

The directive #undef removes a macro name from the list of defined macros (and otherwise does nothing). It is sometimes employed defensively to guard against the possibility of #include inserting a macro name that conflicts with the name of a function the programmer has defined. The convention employed to minimize the likelihood of this occurrence is to use upper case letters for the names of macros and for nothing else.

The sequential bypass strategy employed by the C preprocessor precludes iterative loop directives, but a limited if-else selection is provided. The directive for this purpose, #if, employs a condition test with computation limited to integer constants, character constants, comparisons, arithmetic and logic operators, and macros names (which are expanded before the condition test is calculated). The section of code selected by a #if directive consists of the lines of code that follow it, continuing until one of the directives #else, #endif, or #elif is encountered. The section of code selected by a #if directive is processed if the preprocessor calculates the value of the condition test to be non-zero; otherwise, the preprocessor omits the section from the code sent to the compiler. The term used to describe this procedure is conditional compilation since the condition test determines whether or not a section of code is sent on to the compiler; for example,

#define CFLAG 1
#if CFLAG==1
   display_printf(1,3,"On target ");
#else
   printf("On target\n");
#endif

selects which form of printf to use according to how CFLAG is #defined. CFLAG serves as a "configuration variable" to be set according to the ennvironment where the program will be run.

#elif has the same interpretation as "else if" and requires a condition test. The section of code selected by #elif is terminated by any one of #else, #endif, or #elif also.

#else has the same interpretation as "else", with its section of code terminated only by #endif.

The sections of code selected by the two directives #ifdef and #ifndef discussed earlier can be terminated by #else or #elif as well as #endif.

Preprocessor macros can be used to associate a name with a constant that appears in multiple places in code (e.g., PI or LMOTOR), to simplify a C function call, or to provide a debugging capability that can be turned on or off by use of conditional compilation, among many other possibilities.

Preprocessor Include

The preprocessor #include directive is used to insert either a system header file or a user defined header file into program code. System header files typically provide the prototypes for the pre-compiled functions in standard C library. They also provide configuration variables in the form of macros (e.g., NULL). Some programming environments may include the more common system header files automatically, depending on how much control the programmer is expected to exert over the programming environment. System header files incorporate #ifndef statements to avoid introducing duplicated definitions, since a system header file may appear in more than one user header file included by a function.

If the file name for a #include directive is enclosed in "pointy brackets" (< >) then the preprocessor searches for the file in the system's directory of header files.

If the file name for #include directive is enclosed in (double) quote marks, then the preprocessor searches for the file in the user's file space. Unless the file is located along the user's file path, the path information for locating the file must also be included (e.g., for a USB stick). The contents of the file can be anything, and will be passed through the C preprocessor while being inserted, which will process any preprocessor directives in the file. For user header files, care needs to be taken with any variable declarations incorporated into the file (to avoid duplicates caused by #includes across multiple program files) and in general function definitions should not be imbedded in header files rather than pre-compiled for a user library. To sum up, when #include is used with a carelessly prepared user header file, a compiler error such as a duplicated global variable name may occur, or a preprocessor error such as a duplicated macro name may occur.

The following provides an example of each kind of #include directive:

#include <stdio.h> // insert function prototypes for system I/O functions
#include "mylib.c" // insert my function library

A few of the more commonly used system header files providing definitions for pre-compiled functions in the standard C library follow:

<stdlib.h>  [numeric conversion, memory allocation, functions such as rand]
<stdio.h>   [I/O functions such as printf]
<math.h>    [math functions such as sqrt]
<string.h>  [string functions such as strcpy]
<time.h>    [date and time functions such as time]
<stdarg.h>  [functions with varying number of arguments (i.e., ones using the ... argument)]

Preprocessor Macros

The #define preprocessor directive specifies a macro definition. The macro definition is limited to one line of code (of indefinite length). Once defined the macro will be expanded wherever it occurs in subsequent program code.

Macros are often used to provide replacement text, where the macro provides a more meaningful name; e.g.,

#define RIGHT_MOTOR 0 // equate RIGHT_MOTOR with 0
#define POWER 90 // equate POWER with 90

If the motor command

motor(RIGHT_MOTOR, POWER);

is used in subsequent code, the preprocessor will expand the RIGHT_MOTOR and POWER macros, replace them with 0 and 90, respectively, so the code as prepared for compilation becomes

motor(0, 90);

Global variables could also be used to provide meaningful names for quantities, but preprocessor macros produce slightly more efficient code. The primary advantage in either case is that if testing shows that the motor port or power needs to be changed, it only needs to be changed at one place in the program.

The definition of a preprocessor macro can also specify one or more arguments to be used in expanding the macro. For example,

#define GO_RIGHT(power) motor(RIGHT_MOTOR,power)

defines a macro GO_RIGHT that takes an argument (power) and uses the macro RIGHT_MOTOR in its definition.

If GO_RIGHT(85) appears in program code, it will expand to motor(RIGHT_MOTOR(85) which will then expand to motor(0,85) as the code prepared for compilation.

Superficially, the use of a macro that doesn't have arguments looks like a global variable reference. Likewise, the use of a macro with arguments looks like a call to a function. However, macro expansion is simply one-time text replacement which occurs during preprocessing. Compilation resolves global references as memory locations subject to dynamic change during program execution. When called, a function evaluates and interprets its arguments dynamically.

Appropriate use of macros can make it easier to follow the C program logic and can be used to facilitate program testing and modification.

Conditional Compilation

The C preprocessor can be used to select code to be compiled based on logical conditions in preparing a program for compilation. This is called conditional compilation. Conditional compilation is used to select the code that is to be incorporated into a program based on a condition test. For example, unless a macro named DEBUG has been defined (usually empty; i.e., #define DEBUG), the precompiler can omit code whose only purpose is for debugging. In particular,

#ifdef DEBUG
  printf("Going Left\n");
  beep();
#endif

generates the debugging printf and beep only if DEBUG has been defined, in which case the message "Going Left" will be printed and KRC will beep when program execution reaches this part of its code. If DEBUG is not defined, the preprocessor will leave the code out and the debugging alert will not occur as the program executes.

Macros can also be conditionally defined; for example, the somewhat more sophisticated debugging macro definition

#ifdef DEBUG // if DEBUG is defined, SHOW(printf("%d",i) generates DEBUG code
  #define SHOW(x) printf("DEBUG: "); x // if case: SHOW macro is defined to generate code
#else // if DEBUG is not defined, SHOW(printf("%d",i) generates nothing
  #define SHOW(x) // else case: SHOW macro is defined to generate nothing
#endif

defines a SHOW macro in one of two ways, either to produce debugging printf statements from its argument, or to produce nothing, regardless of argument.

If DEBUG has been defined, then a printf debugging statement used as the argument for SHOW will be incorporated into the program code. Debugging is activated by adding the #define DEBUG directive to the program, and deactivated by commenting it out. In particular, when DEBUG has been defined the code

SHOW(printf("%d\n",i);)

inserted at an appropriate point in the program will be expanded by the preprocessor to produce the code

printf("DEBUG: "); printf("i=%d\n",i);)

Absent a definition for DEBUG the macro SHOW will expand to produce no code at all.

If the value of i is 8 and DEBUG has been defined, the expansion of SHOW illustrated above will generate code which outputs the debugging display "DEBUG: i=8".

The C Library Math Functions

The standard C library has a large number of precompiled math functions. #include <math.h> provides the function prototypes for the math functions that operate on floating point numbers, which is most of them. #include <stdlib.h> provides the function prototypes for those which operate on integers. Arguments for trigonometric functions use radian measure for angles rather than degrees (1 degree is 2π/360 radians). The following is a representative list for the available math functions. For more information about what math functions are available, consult a C reference.

acos    [Category: Math]
Format: double acos(double x);
Returns the angle between 0 and π whose cosine is x, where -1 ≤ x ≤ 1. result is in radians.
asin    [Category: Math]
Format: double asin(double x);
Returns the angle between -π/2 and π/2 whose sine is x, where -1 ≤ x ≤ 1. result is in radians.
atan    [Category: Math]
Format: double atan(double x);
Returns the angle between -π/2 and π/2 whose tangent is x. result is in radians.
cos    [Category: Math]
Format: double cos(double angle);
Returns cosine of angle. Angle is specified in radians; result is between -1 and 1.
exp    [Category: Math]
Format: double exp(double x);
Returns ex (e is the Euler constant, the base for the natural logarithm of a number).
exp10    [Category: Math]
Format: double exp10(double x);
Returns 10x.
log    [Category: Math]
Format: double log(double x);
Returns loge(x), the natural logarithm of x.
log10    [Category: Math]
Format: double log10(double x);
Returns log10(x), the base 10 logarithm of x.
pow    [Category: Math]
Format: double pow(double x, double y);
Returns xy.
rand   [Category: Math]
Format: int rand();
Returns a (pseudo) random integer between 0 and a very large integer established on system installation, drawn from the (pseudo) random number stream initiated at start of program execution according to a "seed" value. rand()%m restricts the range from 0 to m-1. srand is used to re-seed random number generation to vary the random number stream used.
sin    [Category: Math]
Format: double sin(double angle);
Returns the sine of angle. angle is specified in radians; result is between -1 and 1.
sqrt    [Category: Math]
Format: double sqrt(double x);
Returns √x, the square root of x.
srand    [Category: Math]
Format: void srand(int num);
Re-seeds (pseudo) random number generation (the initial seed at start of program execution is srand(1)). Unless it is desirable that a random number stream be reused, the seed value used should be unpredictable from one run to the next. The usual way to accomplish this is to draw a value from the system clock to use as seed; e.g., int seednum = time(NULL);. (the time function is declared in system include file <time.h> and if used with a NULL operand returns elapsed time in (whole) seconds since system start).
tan    [Category: Math]
Format: double tan(double angle);
Returns the tangent of angle. angle is specified in radians; result is between -∞ and +∞.

File I/O for a USB Flash Drive Plugged into the KRC

Before the Linux operating system can access a file system, it has to "mount" the file system. When a USB drive is plugged into the KRC, it is automatically mounted. When the USB drive is unplugged it is automatically unmounted. It can also be unmounted (EJECTED) using the KRC desktop's file manager for file safety. The C Library has a number of functions designed to access files located in mounted file systems. The library functions fprintf and fscanf respectively provide a straight forward means for writing formatted output to a file on a USB drive plugged into the KRC, and for reading formatted data from a file on the USB drive. There are a number of file processing commands, including ones for accessing files byte by byte. For a full description of the range of functions available consult a standard C reference book.

To access a file, in addition to the file name, the directory "path" leading to the file has to be known. For a USB drive the path always starts with

/media/pi/

USB drives have their own names, so for a USB drive named "myUSB", the path would be

/media/pi/myUSB

To determine the USB drive name you will have to exit the UI to the Linux desktop ("Hide UI" under the UI's settings options). Click the lower left corner of the display to bring up the desktop menu and select "System Tools". Choose either "LXterminal" or the file manager. For a terminal, type in the Linux command "ls /media/pi" to see the mount names, the newest of which will be your USB drive. For the file manager, the "Places" list identifies the places the file manager can access, which will include the USB drive. When the USB drive is selected, the path used to access it is displayed along with the file manager's display panel.

Files are accessed in C via a pointer of type FILE, which is defined in the system header file <stdio.h>. The pointer for a file is established when the file is "opened" for access. If the fopen function returns a NULL pointer, it indicates that either the file doesn't exist for the specified file path, or its file system hasn't been mounted (e.g., the USB drive has not been plugged in). Both cases are illustrated in the following program for a USB drive plugged into a KRC. The example otherwise is a program designed to send data to a file, close the file, then reopen the file and retrieve the data to verify a successful write operation. If the file doesn't exist it is created. If it does exist, it is appended to. A user defined preprocessor macro "USBFILE" is constructed to set the file path and file name for the USB drive, illustrating how the preprocessor can be used to potentially simplify program code.

#include <stdio.h>  // make sure file I/O is defined
// USBFILE defines the directory path and file for a mounted USB drive
#ifndef USBFILE
   // change "myUSB" to the name obtained from "ls /media/pi"
   #define USBNAME myUSB
   // _STRINGIFY_ is an auxiliary macro which converts the argument into a string (by surrounding it with double quote marks) using the preprocessor stringification operator "#"
   // _xSTRINGIFY_ forces the expansion of x before the substitution for x is enclosed in double quote marks
   #define _STRINGIFY_(x) _xSTRINGIFY_(x)
   #define _xSTRINGIFY_(x) #x
   // the USBFILE macro appends x to the path for the USB drive, then uses _STRINGIFY_ to make the text a string
   #define USBFILE(x) _STRINGIFY_(/media/pi/USBNAME/x)
#endif
int main()
{
   FILE *f; // file pointer f (the macro defining the FILE data type is in <stdio.h>)
   // A file for the USB drive named "myfile" is set up using macro USBFILE
   char s[81], chkf[] = USBFILE(myfile); // set up the file path for myfile in string variable chkf
   int x, i, data = 2;
   // try opening for read ("r") to see if the file exists
   if ((f = fopen(chkf,"r")) != NULL) {
      fclose(f); // file chkf already exists
      printf("Will be appending to USB %s\n", chkf);
   }
   // (chkf is not open at this point)
   // open to append ("a"), which also tests if the USB stick is plugged in
   if ((f = fopen(chkf,"a")) == NULL) {
      printf("No USB stick detected\n");
      return -1; // exit the program
   }
   // file is now open for append; if it didn't exist it has been created
   printf("Sending data: %s, %d\n", "Field ", data);
   fprintf(f,"Field "); // use fprintf to send a text string to chkf
   fprintf(f,"%d",data); // now send formatted numeric data using fprintf
   fclose(f); // close the file to make sure the output is sent
   // now read the data back in, record by record, quit on end of file (EOF)
   i = 0;
   f = fopen(chkf,"r"); // it exists since we just created it or opened for append successfully
   while(EOF != fscanf(f,"%s %d",s, &x)) { // read a pair of data items from the file
      printf("Record %d: %s, %d\n", i++, s, x); // show record read in
   }
   fclose(f); // done with file, so close it
   return 0;
}

The USB drive can now be removed from the KRC. If not already present, there will now be a file named myfile on the USB drive, which can be read using a the file manager to confirm the write operation was successful.

The KIPR Library File

The KIPR Library provides pre-compiled functions for employing the features of the KRC. The prototype declarations for the functions used for Botball are obtained by including them from the KIPR library, #include <kipr/botball.h>.

Refresher for Commonly Used KIPR Library Functions

The most commonly used KIPR Library functions for robot control are ones for accessing sensor ports, operating DC motors, and suspending a program while a motor action is in progress. A quick refresher for the ones typically used follows:


display_printf(<col>, <row>, <printf-arguments>);
The purpose of this function is to display formatted output at a specific (column, row) location on the KRC program console screen using standard printf formatting. The column range for the display is 0 to 41. For the 3 button (A,B,C) console display, the row range is 0 to 9, and for the 6 button (X,Y,Z,A,B,C) console display, the row range is 0 to 7. Excess data printed to a row is truncated (i.e. text does not wrap). Printing "\n" using display_printf will cause undesired display results.
digital(<port#>)
For a switch plugged into the port, returns 0 if the switch is open and returns 1 if the switch is closed. Digital ports are numbered 0-9. Typically used with button (switch) sensors employed in bumpers or to detect limit of travel. Will also work with sensors having an analog response by interpreting the voltage reading as 0 or 1.
analog(<port#>);
For an analog sensor plugged into the port, returns a value scaled to the range 0-4095 representing the analog voltage variation produced by the sensor. Analog ports are numbered 0-5. Light sensors and range finders are examples of sensors used in analog ports.
msleep(<int_msecs>);
Causes the function to pause for the specified number of milliseconds (motor action continues).
mav(<motor_#>, <vel>);
Turns on the specified motor port, maintaining the specified motor velocity using PID (proportional, integral, derivative) motor control. The motor number is an integer in the range 0 to 3. Motor velocity is an integer between -1500 and 1500 where 0 means the motor is off and negative numbers direct the motor to run in the reverse direction for how it is plugged into the motor port.
motor(<motor_#>, <motor_power>);
Turns on the specified motor port at the PWM power level specified, where a positive power value operates the motor in the direction as fd and a negative value operates it in the reverse direction. The motor number is an integer in the range 0 to 3. The power setting is -100 to 100 representing percentage of full power. Motor response with respect to power is non-linear (i.e., doubling power does not double motor velocity). Moreover, motor velocity for a power setting varies according to load. In contrast, PID motor control varies the PWM power applied to try to establish and maintain a specified motor velocity.
fd(<motor_#>);
Turns of the specified motor port at maximum PWM (pulse width modulation) power for the motor's forward direction for how the motor is plugged in. The motor number is an integer between 0 and 3
bk(<motor_#>);
Turns of the specified motor port at maximum PWM (pulse width modulation) power for the motor's reverse direction for how the motor is plugged in. The motor number is an integer between 0 and 3
off(<motor_#>);
Turns off the specified motor port. Once a motor port is turned on, a motor plugged into the port will continue operating until the motor port is turned off or the program terminates (which turns off all motor ports).
ao();
Turns all motor ports off.

KIPR Library Functions

The following is a comprehensive list of the pre-compiled functions provided by the KIPR Library. The library functions for using a USB camera or a graphics window are presented separately.

a_button    [Category: Sensors]
Format: int a_button();
Returns the state of the A button on the KRC program console (1 if pressed, 0 otherwise).
a_button_clicked    [Category: Sensors]
Format: int a_button_clicked();
Returns the state of the A button on the KRC program console (1 of pressed, 0 otherwise). Continued function execution is blocked while the button is pressed. The construction
while (a_button()==0) {
while (a_button()==1); ...} //debounce A button
is equivalent to
while (a_button_clicked()==0) {...}
accel_x    [Category: Sensors]
Format: int accel_x();
Returns the value of the accelerometer in its x direction relative to the horizontal plane of the KRC (left to right). Values are scaled to the acceleration produced according to the gravitational constant G where 1G = 512. At rest with the KRC flat on the table the value when calibrated is 0.
accel_y    [Category: Sensors]
Format: int accel_y();
Returns the value of the accelerometer in its y direction relative to the horizontal plane of the KRC (back to front). Values are scaled to the acceleration produced according to the gravitational constant G where 1G = 512. At rest with the KRC flat on the table the value when calibrated is 0.
accel_z    [Category: Sensors]
Format: int accel_z();
Returns the value of the accelerometer for its vertical, or z direction, relative to the horizontal plane of the KRC (bottom to top). At rest with the KRC flat on the table the value when calibrated is -1G or -512 (which denotes your acceleration towards the center of the Earth to keep you from flying off of the planet).
accel_calibrate    [Category: Sensors]
Format: int accel_calibrate();
Used with the KRC flat on the table to calibrate the base accelerometer readings as accel_x()=0, accel_y=0, accel_z=-512.
alloff    [Category: Motors]
Format: void  alloff();
Turns off all motor ports. ao is a short form for alloff.
analog    [Category: Sensors]
Format: int analog(int p);
Returns the value of the sensor plugged into analog port p scaled as an integer between 0 and 4095. Analog ports on the KRC are numbered 0 through 9. .
analog8    [Category: Sensors]
Format: int analo8(int p);
8-bit version of analog. Returns the value of the sensor plugged into analog port p scaled as an integer between 0 and 255. Analog ports on the KRC are numbered 0 through 9.
analog10    [Category: Sensors]
Format: int analog10(int p);
Returns the value of the sensor plugged into analog port p scaled as an integer between 0 and 1023. Analog ports on the KRC are numbered 0 through 9.
any_button    [Category: Sensors]
Format: int  any_button()();
Returns 1 if any button (A,B,C,X,Y,Z,Side) is pressed.
For example,
display_printf(0,0,"Press any button to start");
while (any_button()==0);
while (a_button()==0) {
   display_printf(0,0,"Press A button to stop ");
   if (a_button() == 1) break;
}
will start when any button is pressed, and even if A was pressed to start, the program continues to run until A is pressed again.
ao    [Category: Motors]
Format: void ao();
Turns off all motor ports (same as alloff).
b_button    [Category: Sensors]
Format: int b_button();
Returns the state of the B button on the KRC program console (1 if pressed, 0 otherwise).
b_button_clicked    [Category: Sensors]
Format: int b_button_clicked();
Returns the state of the B button on the KRC program console (1 of pressed, 0 otherwise). Continued function execution is blocked while the button is pressed. The construction
while (b_button()==0) {
while (b_button()==1); ...} //debounce B button
is equivalent to
while (b_button_clicked()==0) {...}
beep    [Category: Output]
Format: void beep();
Produces a tone from the KRC loud speaker. Returns when the tone is finished.
bk    [Category: Motors]
Format: void bk(int m);
Turns on the motor plugged into motor port m at full PWM power in the reverse direction (red light comes on for the KRC motor port). Motor ports are numbered from 0 to 3.
Example:
bk(3); // full power reverse for motor 3
block_motor_done    [Category: Motors]
Format: void block_motor_done(int m);
If a motor positioning function is in progress for the motor plugged into motor port m, continued function execution is blocked until the motor has reached its goal position. Motor positioning functions are move_to_position (or mtp) and move_relative_position (or mrp).
Example:
mtp(0,500,20000); // turn on motor 0 at 500 ticks/sec and stop when position 20000 is reached
block_motor_done(1);
// pause until mtp has reached its goal position of 20000 for motor 1
This function must be used with some care, since if something prevents the motor from reaching its goal position, the program will hang until halted by external means.
bmd    [Category: Motors]
Format: void bmd(int m);
This function is the same as block_motor_done, just under a (much) shorter name.
c_button    [Category: Sensors]
Format: int c_button();
Returns the state of the C button on the KRC program console (1 if pressed, 0 otherwise).
c_button_clicked    [Category: Sensors]
Format: int c_button_clicked();
Returns the state of the C button on the KRC program console (1 of pressed, 0 otherwise). Continued function execution is blocked while the button is pressed. The construction
while (c_button()==0) {
while (c_button()==1); ...} //debounce C button
is equivalent to
while (c_button_clicked()==0) {...}
clear_motor_position_counter    [Category: Motors]
Format: void clear_motor_position_counter(int motor_nbr);
Reset the position counter for the motor specified to 0.
console_clear    [Category: Output]
Format: void console_clear();
Clear the KRC program console print buffer. See also display_clear.
digital    [Category: Sensors]
Format: int digital(int p);
Returns the value of the sensor in digital port p, as a true/false value (1 for true and 0 for false). Sensors are expected to be active low, meaning that they are valued at zero volts in the active, or true, state. Thus the library function returns the inverse of the actual reading from the digital hardware. If the reading is zero volts or logic zero, the digital function will return true. Digital ports on the KRC are numbered 0 through 9.
disable_servo    [Category: Servos]
Format: void disable_servo(int p);
Disables the specified servo port. Servo ports are disabled by default, and if enabled, consume power whenever a motor is plugged into them. Servo ports on the KRC are numbered 0 to 3.
disable_servos    [Category: Servos]
Format: void disable_servos();
Disables all servo motor ports (powers down all servo motors). Servo ports are disabled by default, and if enabled, consume power whenever a motor is plugged into them.
display_clear    [Category: Output]
Format: void display_clear();
Clear the KRC program console screen for using display_printf. See also console_clear. display_clear() initializes the display buffer as all spaces. console_clear() clears the display, not the display buffer.
display_printf    [Category: Output]
Format: void display_printf(int col, int row, char s[], ... );
The purpose of this function is to display formatted output at a specific (column, row) location on the KRC program console screen using standard printf formatting. The column range for the display is 0 to 41. For the 3 button (A,B,C) console display, the row range is 0 to 9, and for the 6 button (X,Y,Z,A,B,C) console display, the row range is 0 to 7. Excess data printed to a row is truncated (i.e. text does not wrap). Printing "\n" using display_printf will cause undesired display results.
enable_servo    [Category: Servos]
Format: void enable_servo(int p);
Enables specified servo port. Servo ports are disabled by default, and if enabled, consume power whenever a motor is plugged into them. If a servo position for a port hasn't been set when it is enabled, the default position is 1024, the mid-point of servo travel from 0 to 2047. Servo ports on the KRC are numbered 0 to 3.
enable_servos    [Category: Servos]
Format: void enable_servos();
Enables all servo motor ports. Servo ports are disabled by default, and if enabled, consume power whenever a motor is plugged into them.
extra_buttons_show    [Category: Output]
Format: void extra_buttons_show();
Shows the X, Y, and Z buttons on the KRC program console screen above the A, B, and C buttons. Note: this reduces the display area for printf and display_printf. See also extra_buttons_hide, get_extra_buttons_visible.
extra_buttons_hide    [Category: Output]
Format: void extra_buttons_hide();
Hides the X, Y, and Z buttons on the KRC program console screen. Note: this is the default display configuration. See also extra_buttons_show, get_extra_buttons_visible.
fd    [Category: Motors]
Format: void fd(int m);
Turns on the motor plugged into motor m at full PWM power in the forward direction (green light comes on for the KRC motor port). Motors are numbered 0 to 3.
Example:
fd(3); // full power forward for motor 3
get_extra_buttons_visible    [Category: Sensors]
Format: int get_extra_buttons_visible();
Returns 1 if the X, Y, and Z buttons are visible, 0 if not. See also extra_buttons_show, extra_buttons_hide
get_motor_done    [Category: Motors]
Format: int get_motor_done(int m);
For a motor moving to a goal position set by a motor positioning command, returns 1 if the motor has reached the goal position and 0 otherwise. Motor positioning functions are move_to_position (or mtp) and move_relative_position (or mrp). See also block_motor_done.
get_motor_position_counter    [Category: Motors]
Format: int get_motor_position_counter(int m);
Returns the current motor position for motor m in "ticks" (a value which is continually being updated for each motor using PID motor control based on back EMF; a typical discrimination for a given motor is on the order of 1400 position "ticks" per rotation)
get_servo_enabled    [Category: Servos]
Format: int get_servo_enabled(int srv);
Returns 1 if the specified servo port is enabled and 0 otherwise. Servo ports on the KRC are numbered 0 to 3. See also enable_servo, disable_servo.
get_servo_position    [Category: Servos]
Format: int get_servo_position(int srv);
Returns the last position value set for the servo in port srv. The value will be in the range 0 to 2047. Servo ports on the KRC are numbered 0 to 3. See also set_servo_position.
mav   [Category: Motors]
Format: void mav(int m, int vel);
This function is the same as move_at_velocity
motor    [Category: Motors]
Format: void motor(int m, int p);
Turns on motor port m at p% of full PWM motor power. The range for p is -100 to 100, where a negative value runs the motor in its reverse direction. On the KRC, a motor port supplying forward power lights green, and for reverse lights red, although actual motor direction depends on how it is plugged in.
move_at_velocity    [Category: Motors]
Format: void move_at_velocity(int m, int vel);
Turns on motor port m, varying applied PWM power using PID motor control to maintain motor velocity vel indefinitely. The velocity range is -1500 to 1500 ticks per second. On the KRC, a motor port supplying forward power lights green, and for reverse lights red, although actual motor direction depends on how it is plugged in.
move_relative_position    [Category: Motors]
Format: void move_relative_position(int m, int absvel, int pos);
Turns on motor port m, varying applied PWM power using PID motor control to maintain motor velocity absvel (±) until the motor has moved from its current position cp to the goal position cp + pos. The range for the absvel argument is 0 to 1500 ticks per second. On the KRC, a motor port supplying forward power lights green, and for reverse lights red, although actual motor direction depends on how it is plugged in.
Example:
move_relative_position(1,275,-1100);
move_to_position    [Category: Motors]
Format: void move_to_position(int m, int absvel, int pos);
Turns on motor port m, varying applied PWM power using PID motor control to maintain motor velocity absvel (±) until the motor has moved from its current position to goal position pos. The range for the absvel argument is 0 to 1500 ticks per second. On the KRC, a motor port supplying forward power lights green, and for reverse lights red, although actual motor direction depends on how it is plugged in. If the motor is already at the goal position pos, the motor doesn't move.
mrp    [Category: Motors]
Format: void mrp(int m, int vel, int pos);
This function is the same as move_relative_position.
mtp  [Category: Motors]   
Format: void mtp(int m, int vel, int pos);
This function is the same as move_to_position.
msleep    [Category: Time]
Format: void msleep(int msec);
Suspends function execution for the amount of time specified in milliseconds.
Example:
msleep(1500); // wait for 1.5 seconds
off    [Category: Motors]
Format: void off(int m);
Turns off motor port m.
Example:
off(1); // turn off motor 1
power_level    [Category: Sensor]
Format: double power_level();
Returns the current power level for the KRC battery as a value between 0 and 1.
seconds    [Category: Time]
Format: double seconds();
Returns the number of seconds that have elapsed since system start up according to the system's internal clock. Resolution is 0.001 seconds (one millisecond).
set_a_button_text    [Category: Sensors]
Format: void set_a_button_text char txt[]);
This function resets the text displayed on the A button to be the text string specified rather than the default string "A".
set_b_button_text    [Category: Sensors]
Format: void set_b_button_textchar txt[]);
This function resets the text displayed on the B button to be the text string specified rather than the default string "B".
set_c_button_text    [Category: Sensors]
Format: void set_c_button_textchar txt[]);
This function resets the text displayed on the C button to be the text string specified rather than the default string "C".
set_digital_output    [Category: Output]
Format: void set_digital_output(int port, int inout);
Digital ports on the KRC can be configured for either input or output. By default digital ports are set for input. If the value of inout is 1, the port is configured for output, for example, the statement
set_digital_output(9,1);
when executed configures digital port 9 for output. The port number specified must be in the range from 0 to 9. See also set_digital_value. See below for an example program using digital output.
set_digital_value    [Category: Output]
Format: void set_digital_value(int port, int value);
Sets the (SEN) value for the specified port on the KRC to either 0 (low) or 1 (high). For the sensor ports on the KRC, the SEN rail is the inner rail having a single row of sockets. The specified port must be in the range 0 to 9. The library function set_digital_ouput is used to configure a digital port on the KRC for output (or to re-configure it for input).

As an example, if the anode lead for an LED (the longer of its two leads) is plugged into the SEN socket for digital port 9 and its cathode lead into the GND rail (the outer rail for the PWR/GND rails), then if digital port 9 has been set for output
set_digital_value(9,1);
will turn on the LED when executed and
set_digital_value(9,0);
will turn it off. See below for an example program using digital output.
set_servo_position    [Category: Servos]
Format: int set_servo_position(int srv, int pos);
Sets the position value of the servo in port srv. The value of pos must be in the range 0 to 2047. The KRC has 4 servo ports numbered 0 to 3. A servo motor consumes maximum power when trying to reach a position, so if the servo cannot reach the position specified, its power consumption will rapidly pull the KRC battery down. Servo motors tend to reach their limit of travel around 200 short of either end of the 0 to 2047 range, so values close to the limit of travel should be avoided. Additionally, servo travel is not smooth, and positions tend to be about 5 units apart on the 0 to 2047 scale; i.e., position increments should be at least 5. See also get_servo_position.
set_x_button_text    [Category: Sensors]
Format: void set_x_button_text(char txt[]);
This function resets the text displayed on the X button to be the text string specified rather than the default string "X". See also extra_buttons_show, extra_buttons_hide, get_extra_buttons_visible.
set_y_button_text    [Category: Sensors]
Format: void set_y_button_text(char txt[]);
This function resets the text displayed on the Y button to be the text string specified rather than the default string "Y". See also extra_buttons_show, extra_buttons_hide, get_extra_buttons_visible.
set_z_button_text    [Category: Sensors]
Format: void set_z_button_text(char txt[]);
This function resets the text displayed on the Z button to be the text string specified rather than the default string "Z". See also extra_buttons_show, extra_buttons_hide, get_extra_buttons_visible.
side_button (or black_button)   [Category: Sensors]
Format: int side_button();
Returns the state of the (physical) side button on the KRC (1 if pressed, 0 otherwise).
side_button_clicked    [Category: Sensors]
Format: int side_button_clicked();
Returns the state of the (physical) side button on the KRC (1 if pressed, 0 otherwise). Continued function execution is blocked while the button is pressed. The construction returns 1 for pressed, 0 for not pressed. The construction
while (side_button()==0) {
while (side_button()==1); ...} //debounce A button
is equivalent to
while (a_button_clicked()==0) {...}
thread_create    [Category: Threads]
Format: thread thread_create(<function name>);
The thread_create function is used to create a thread for running a function in parallel to main, returning a thread ID value of type thread. The special data type thread is for the thread ID's created by the system to keep track of active threads. Note that the returned value must be assigned to a variable of type thread to remain available to the program. When a function is run in a thread (via thread_start),the thread will remain active until the function finishes or the thread is destroyed (via thread_destroy). If the thread hasn't been destroyed, it can be started again; otherwise, a new thread has to be created for the function to be run in a thread again. Since the system limits how many threads can be created, thread management is important; i.e., threads should be destroyed once they have finished (if threads seem to stop working it is usually because too many have been created). See the section on threads below for additional information and an example.
thread_destroy    [Category: Threads]
Format: void thread_destroy(thread tid);
The thread_destroy function is used to destroy a thread created for running a function in parallel to main. The thread is destroyed by passing its thread ID to thread_destroy. If the function for the thread is running in the thread when the thread is destroyed, its execution is terminated. See the section on threads below for additional information and an example.
thread_start    [Category: Threads]
Format: void thread_start(thread tid);
The thread_start function is used to activate the thread given by tid, running its associated function in the thread in parallel with main and any other active threads. The value of argument tid must have a thread ID value as returned by thread_create. Keep in mind that thread IDs generated by thread_create must be retained in variables of type thread to remain available for later use in a program. The thread is active until its function finishes or until it is terminated by thread_destroy. A global variable can be used as a flag for a function running in a thread to signal when it is done.

The following example shows the main process creating a thread for the function check_sensor, running the function in the thread, and then destroying the thread one second later (whether or not the thread is still active):

int main()
{
    thread tid;  // variable for holding a thread's ID
    tid = thread_create(check_sensor);  // capture thread's ID in tid
    thread_start(tid);  // run check_sensor in the thread
    msleep(1000);  // let it keep running for a second
    thread_destroy(tid);  // stop check_sensor if still running
}

See the section on threads below for a complete example using threads.
thread_wait    [Category: Threads]
Format: void thread_wait(thread tid);
If thread_wait is executed in a function, it will block continued execution of the function until the function running in the thread given by tid has finished (or until the thread given by tid is destroyed by some other process). If the thread given by tid is inactive, the function does nothing. thread_wait is used for synchronization of multiple threads (e.g., thread 1 doesn't continue until thread 2 is done). See the section on threads below for additional information and an example.
x_button    [Category: Sensors]
Format: int x_button();
Returns the state of the X button when it is visible on the KRC program console (1 if pressed, 0 otherwise). This button is an extra button. Use extra_buttons_show to show the X, Y, and Z buttons. See also extra_buttons_hide, get_extra_buttons_visible.
x_button_clicked    [Category: Sensors]
Format: int x_button_clicked();
Returns the state of the X button when it is visible on the KRC program console (1 if pressed, 0 otherwise). Continued function execution is blocked while the button is pressed. The construction
while (x_button()==0) {
while(x_button()==1); ...} //debounce X button
is equivalent to
while(x_button_clicked()==0) {...}
This button is an extra button. Use extra_buttons_show to show the X, Y, and Z buttons. See also extra_buttons_hide, get_extra_buttons_visible.
y_button    [Category: Sensors]
Format: int y_button();
Returns the state of the Y button when visible on the KRC program console (1 if pressed, 0 otherwise). This button is an extra button. Use extra_buttons_show to show the X, Y, and Z buttons. See also extra_buttons_hide, get_extra_buttons_visible.
y_button_clicked    [Category: Sensors]
Format: int y_button_clicked();
Returns the state of the Y button when it is visible on the KRC program console (1 if pressed, 0 otherwise). Continued function execution is blocked while the button is pressed. The construction
while (y_button()==0) {
while(y_button()==1); ...} //debounce Y button
is equivalent to
while(y_button_clicked()==0) {...}
This button is an extra button. Use extra_buttons_show to show the X, Y, and Z buttons. See also extra_buttons_hide, get_extra_buttons_visible.
z_button    [Category: Sensors]
Format: int z_button();
Returns the state of the Z button when visible on the KRC program console (1 if pressed, 0 otherwise). This button is an extra button. Use extra_buttons_show to show the X, Y, and Z buttons. See also extra_buttons_hide, get_extra_buttons_visible.
z_button_clicked    [Category: Sensors]
Format: int z_button_clicked();
Returns the state of the Z button when it is visible on the KRC program console (1 if pressed, 0 otherwise). Continued function execution is blocked while the button is pressed. The construction
while (z_button()==0) {
while(z_button()==1); ...} //debounce Z button
is equivalent to
while(z_button_clicked()==0) {...}
This button is an extra button. Use extra_buttons_show to show the X, Y, and Z buttons. See also extra_buttons_hide, get_extra_buttons_visible.

Example program for lighting an LED plugged into a digital port

By default the digital ports for the KRC are configured for input. The KIPR Library function set_digital_output is used to configure digital port direction; for example,

set_digital_output(9, 1);

configures port 9 for output and

set_digital_output(9, 0);

configures it for input.

For a digital port configured for output, the KIPR Library function set_digital_value is used to set the port's output value to either 0 (low) or 1 (high); for example,

set_digital_value(9, 1);

sets the output value for port 9 high.

If you have a typical 5mm LED on hand, you can use the following program to operate it. An LED will "turn on" when voltage applied to its anode lead rises above a prescribed level (typically between 1.9 and 3.2V, depending on color). If too much current is passed through the LED, it will burn out (the typical spec is 20-30mA). For the KRC, digital outputs on the SEN rail are sufficiently current limited to operate an LED without burning it out.

The LED's anode is normally identified by having the longer of the two leads. Additionally, the flange at the base of the LED is normally flattened on the cathode side.

#include <stdio.h>
#include <kipr/botball.h>
/*  This is a program to blink an LED plugged into digital port 9 */
int main()
{
   printf("LED in port 9\n");
   printf("Press side button to quit\n");
   set_digital_output(9, 1); // set digital port direction to digital output
   while (side_button() == 0) {
      set_digital_value(9, 1); // set digital output to 1 (high)
      msleep(500);
      set_digital_value(9, 0); // set digital output to 0 (low)
      msleep(500);
   }
   set_digital_output(9, 0); // set digital port direction back to digital input
   printf("\ndone\n");
   return 0;
}

KIPR Vision Library Functions

The KIPR Vision System incorporates color vision tracking and QR code identification. A USB web camera is used to provide images to the KRC at a rate dependent on lighting conditions but exceeding 6 frames per second. Using the KRC interface, an arbitrary number of camera configurations containing "channels" for color vision tracking and/or QR code identification can be defined.

For color vision tracking, images are processed by the KRC to identify "blobs" matching the color specification for each color channel in a camera configuration. A blob is a set of contiguous pixels in the image matching the color specification for the channel. The color model used for the specification is HSV (Hue, Saturation, Value). The specification is a numeric range for each of H, S, V to represent those pixels having HSV values that fall within those ranges; e.g., a blob might consist of contiguous pixels that are "reddish" in color.

The UI interface "Settings .. Channels" provides the means for adding a camera configuration to the KRC and for setting up its camera channels. Unless specified otherwise, when a camera function is called, the configuration marked with a yellow star on the UI's configurations panel is the one used by default. New channels are added to the configuration as either "HSV Blob Tracking" or "QR Code Scanning". Channels are numbered 0,1,..., where blob tracking channels are configured by selecting the channel, then proceeding to the "Configure" screen. This brings up the camera image, showing the bounding boxes for the selected channel. Clicking on the image will alter the color specification based on the color clicked on. For many purposes this will suffice, but if further refinement is needed, it can be modified either by dragging the boxes on the HSV panel or by switching to manual mode to show the HSV color ranges, altering them as desired. Live feed from the camera simplifies the process of determining how much of the spectrum is needed to produce blobs matching a color (e.g., if the objective is to identify red objects, the configuration needs to select the part of the HSV spectrum that corresponds to "reddish" in color). What is perceived by the human eye as "red" is actually a color range, which is what HSV specification attempts to duplicate. The spectrum values for the channel are retained with the configuration until the configuration is deleted.

While USB cameras support much higher resolutions, for blob tracking the lower resolution of 160×120 is sufficient for blob tracking, reducing camera overhead for determining blobs (160×120 is 19,200 pixels in 120 rows of 160 pixels each).

KIPR Vision Library functions are used to select a configuration and obtain information about the color blobs being identified by its channels, such as bounding box coordinates and pixel density.

In addition to channels for color tracking, a configuration can have channels for identifying QR (Quick Response) codes. A QR code is essentially a 2-dimensional bar code for compactly representing text data. KIPR Vision Library functions are provided for decoding any QR code in the image. See the QR code vision functions get_object_data, get_object_data_length below.

camera_close    [Category: Vision]
Format: void camera_close();
Cleanup the current camera instance. See also camera_open, camera_open_device.
camera_load_config    [Category: Vision]
Format: int camera_load_config(char name[]);
Loads a config file on the KRC other than the default config file. You must append .config to the name for this function to locate it. Returns 1 on success, 0 on failure. See also camera_open, camera_open_device.
camera_open    [Category: Vision]
Format: int camera_open();
Opens the KRC's default channel configuration. The default configuration is selected from among the channel configurations defined on the KRC using the UI's "Settings..Channels" menu. Returns 1 on success, 0 on failure. See also camera_open_device, camera_close.
camera_update    [Category: Vision]
Format: int camera_update();
Retrieves the current image from the camera for processing. Returns 1 on success, 0 on failure.
get_channel_count    [Category: Vision]
Format: int get_channel_count();
Returns the number of channels in the current configuration. See also get_object_count.
get_camera_frame    [Category: Vision]
Format: const unsigned char* get_camera_frame();
Returns a pointer to the camera frame. The pointer is valid until camera_update() is called again. Frame data is in BGR 888 pixel format, 3 bytes per pixel. It is stored as a 1-dimensional character array where each byte is the 8-bit (unsigned) integer value for each BGR color (blue-green-red). For the resolution used (160×120), the camera frame has length 3×160×120 = 57,600 and has to be processed byte by byte. The following code segment illustrates processing the camera frame using the graphics function graphics_pixel to transfer the frame to a graphics window for display (see the Graphics Library):
int r, c, i;
const unsigned char *img;
camera_update();
img = get_camera_frame();
for(r=0; r<120; r++) {
   for(c=0; c<160; c++) {
      i = 3*(160*r + c); // index of pixel to paint into row r, column c
      graphics_pixel(c, r, img[i+2], img[i+1], img[i]); // RGB order by reversing GBR
   }
}
graphics_update();
get_object_area    [Category: Vision]
Format: int get_object_area(int channel, int);
Returns the object's bounding box area. -1 is returned if the channel or object doesn't exist.
Note: The data types rectangle, point2, and point3 are named struct types for geometric objects specified in the KIPR library.
A variable of type rectangle has 4 components ulx, uly, width, height.
A variable of type point2 has 2 components x, y.
A variable of type point3 has 3 components x, y, z.
get_object_bbox    [Category: Vision]
Format: rectangle get_object_bbox(int channel, int object);
Returns the bounding box of the given object on the given channel as a rectangle data type. For example,
rectangle mybox;
mybox = get_object_bbox(0,2);
printf("x coord %d y coord %d\n", mybox.ulx, mybox.uly); printf("width %d height %d\n", mybox.width, mybox.height);
displays the coordinates of the upper left corner of bounding box 2 for channel 0 along with its width and height.
get_object_center    [Category: Vision]
Format: point2 get_object_center(int channel, int object);
Returns The (x, y) center of the given object on the given channel as a font class="CVariable">point2 data type. For example,
point2 mcenter;
mcenter = get_object_center(0,2);
printf("x center coord %d y center coord %d\n", mcenter.x, mcenter.y);

displays the x and y coordinates of center point of box 2 for channel 0.
get_object_centroid    [Category: Vision]
Format: int get_object_centroid(int channel, point2 object);
Returns The (x, y) coordinates of the centroid of the given object on the given color channel as a font class="CVariable">point2 data type (the centroid is the center of mass for the pixels of the specified color). For example,
point2 mcentroid;
mcentroid = get_object_centroid(0,2);
printf("x centroid coord %d y centroid coord %d\n", mcentroid.x, mcentroid.y);
displays the x and y coordinates of centroid of box 2 for color channel 0. The centroid is NOT the same as the center. It is the center of mass for a blob; e.g., for a color arrow pointing right, there are more pixels to right of center, so the centroid is to the right of center.
get_object_confidence    [Category: Vision]
Format: double get_object_confidence(int channel, int object);
Returns the confidence, between 0.0 and 1.0, that the density of color pixels for the object on the given channel is significant. If the channel or object doesn't exist, 0.0 is returned.
get_object_count    [Category: Vision]
Format: int get_object_count(int channel);
Returns the number of objects being "seen" for the specified channel. Objects are sorted by bounding box area, largest first. Returns -1 if channel doesn't exist. See also get_channel_count.
get_object_data    [Category: Vision]
Format: char *get_object_data(int channel, int object);
Returns the sequence of character data associated with a given object on a QR channel. If there is no data, 0 is returned. The data is not guaranteed to be null terminated, but can be accessed using array notation; for example,
get_object_data(0,0)[0], get_object_data(0,0)[1], etc.
camera_update will invalidate the pointer returned by get_object_data. See also get_object_data_length.
get_object_data_length    [Category: Vision]
Format: int get_object_data_length(int channel, int object);
Returns the number of characters associated with the QR code on a QR channel. If there is no data, 0 is returned. If the channel or object is invalid, 0 is returned. See also get_object_data.

Example program for using a camera to track an object

Camera functions will not provide meaningful data until the camera has been activated using camera_open or camera_open_at_res. Once the camera has been activated, the camera function camera_update is used to obtain the current frame in the camera's field of view. Note that in the program below camera_update is called every time through a camera processing loop to provide a current camera frame for processing.

/* This program points a servo (that is plugged into port 0 and centered on the camera's field of vision) towards an object that fits into the color model defined for channel 0 */ 
#include <stdio.h>
#include <kipr/botball.h>
int main()
{
   int offset, x, y;
   enable_servo(0);      // enable servo
   camera_open();        // activate camera
   camera_update();      // get most recent camera image and process it
   while (side_button() == 0) {
      x = get_object_center(0,0).x;  // get image center x data
      y = get_object_center(0,0).y;  //    and y data
      if (get_object_count(0) > 0) { // there is a blob
         display_printf(0,1,"Center of largest blob: (%d,%d)   ",x,y);
         offset=5*(x-80); // amount to deviate servo from center
         set_servo_position(0,1024+offset);
      }
      else {
         display_printf(0,1,"No object in sight                ");
      }
      msleep(200);       // don't rush print statement update
      camera_update();   // get new image data before repeating
   }
   disable_servos();
   camera_close();
   printf("All done\n");
   return 0;
}

Example program to translate a QR code while showing the camera image in a graphics window

This program displays the camera image in a 160×120 graphics window on the KRC display, decoding any QR code found and displaying the result in the space above the graphics window.
// Assume channel 1 is configured for identifying QR codes
// If a QR code is found, it is translated
#include <stdio.h>
#include <kipr/botball.h>
int main()
{
    int r, c, ix, i, lngth;
    const unsigned char *img; // variable to hold camera image
    camera_open(); graphics_open(160,120); // activate camera and open a graphics window
    camera_update(); // get most recent camera image and process it
    while(side_button()==0) {
        img=get_camera_frame(); // get a camera frame and display it in graphics window
        for(r=0; r<120; r++) {
             for(c=0; c<160; c++) {
                 ix=3*(160*r + c); // index of pixel to paint into row r, column c
                 graphics_pixel(c,r,img[ix+2],img[ix+1],img[ix]); // RGB order by reversing GBR
             }
        }
        graphics_update(); // show the frame
        if (get_object_count(1) > 0) { // there is a QR code in view
            display_printf(0,0,"QR code:                              ");
            lngth = get_object_data_length(1,0); // decode and display above graphics window
            for(i=0; i < lngth; i++) { // print QR code letter by letter until end of data
                display_printf(9+i,0,"%c", get_object_data(1,0)[i]);
            }
        }
        else {
            display_printf(0,0,"No QR code detected                   ");
        }
        camera_update(); // get new image data before repeating 
    }
    camera_close(); graphics_close(); // clean up
    return 0;
}

KRC Graphics Functions

Basic graphical draw operations for the KRC are provided by the KIPR library. These allow a user to create a graphical interface or animated graphics (such as wait_for_light(). Functions are provided to draw and color pixels, lines, circles, triangles, and rectangles (non-rotated). The screen and two dimensional objects can also be filled with a single color. Both mouse clicks and cursor location can be detected (on the KRC, a screen tap corresponds to a left mouse click).

Note that any virtual features of the KRC user interface (such as the A,B,C buttons) will be unavailable if obscured by the graphics window (it is advisable to use the built-in side button when doing graphical applications for this reason). If a USB keyboard is attached to the KRC, the title bar of a graphics window is exposed by pressing <alt><space>d, which allows moving it around on the display. Alternately, the desktop menu has functions for turning titles on or off within its "Other" category.

graphics_open    [Category: Graphics]
Format: int graphics_open(int width, int height);
Opens and centers a graphics window on the display of the specified width and height. The maximum width for the KRC display is 800, and the maximum height is 460. See also graphics_close.
graphics_close    [Category: Graphics]
Format: void graphics_close();
Closes the graphics window on the display, restoring access to any virtual buttons underneath it. See also graphics_open.
graphics_update    [Category: Graphics]
Format: void graphics_update();
Repaints the pixels in the graphics window to show any changes that have been made.
graphics_clear    [Category: Graphics]
Format: void graphics_clear();
Erases the graphics window (not shown until graphics_update).
graphics_fill    [Category: Graphics]
Format: void graphics_fill(int r, int g, int b);
Colors the pixels in the window using the r,g,b color encoding (0 to 255 for each).
graphics_pixel    [Category: Graphics]
Format: void graphics_pixel(int x, int y, int r, int g, int b);
Colors the pixel in column x, row y of the window using the r,g,b color encoding, where columns x and rows y are indexed starting from the upper left corner of the graphics window as (0,0).
graphics_line    [Category: Graphics]
Format: void graphics_line(int x1, int y1, int x2, int y2, int r, int g, int b);
Draws a line in the window from the specified (x1,y1) pixel to the (x2,y2) pixel using the r,g,b color encoding (where columns x1, x2 and rows y1, y2 are indexed starting from the upper left corner (0,0) of the graphics window).
graphics_circle    [Category: Graphics]
Format: void graphics_circle(int cx, int cy, int radius, int r, int g, int b);
Draws a circle of the specified radius with center in the graphics window at (cx,cy) using the r,g,b color encoding (where columns cx and rows cy are indexed starting from the upper left corner (0,0) of the graphics window).
graphics_circle_fill    [Category: Graphics]
Format: void graphics_circle_fill(int cx, int cy, int radius, int r, int g, int b);
Draws a circle of the specified radius with center in the graphics window at (cx,cy) and fills it using the r,g,b color encoding (where columns x1, x2 and rows y1, y2 are indexed starting from the upper left corner (0,0) of the graphics window).
graphics_rectangle    [Category: Graphics]
Format: void graphics_rectangle(int x1, int y1, int x2, int y2, int r, int g, int b);
Draws a rectangle with upper left corner (x1,y1) and lower right corner (x2,y2) in the graphics window using the r,g,b color encoding (where columns x1, x2 and rows y1, y2 are indexed starting from (0,0) the upper left corner of the graphics window).
graphics_rectangle_fill    [Category: Graphics]
Format: void graphics_rectangle_fill(int x1, int y1, int x2, int y2, int r, int g, int b);
Draws a rectangle with upper left corner (x1,y1) and lower right corner (x2,y2) in the graphics window and fills it using the r,g,b color encoding (where columns x1, x2 and rows y1, y2 are indexed starting from (0,0) the upper left corner of the graphics window).
graphics_triangle    [Category: Graphics]
Format: void graphics_triangle(int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b);
Draws a triangle with corners (x1,y1), (x2,y2), (x3,y3) in the graphics window using the r,g,b color encoding (where columns x1, x2, x3 and rows y1, y2, y3 are indexed starting from (0,0) the upper left corner of the graphics window).
graphics_triangle_fill    [Category: Graphics]
Format: void graphics_triangle_fill(int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b);
Draws a triangle with corners (x1,y1), (x2,y2), (x3,y3) in the graphics window and fills it using the r,g,b color encoding (where columns x1, x2, x3 and rows y1, y2, y3 are indexed starting from (0,0) the upper left corner of the graphics window).
get_mouse_position    [Category: Graphics]
Format: void get_mouse_position(int *x, int *y);
Assigns the column,row position of the cursor in the window to the two specified address parameters. Note that the typical call for this function will look like
get_mouse_position(&col, &row);
get_mouse_left_button    [Category: Graphics]
Format: int get_mouse_left_button();
Returns 1 if the left mouse button is clicked or if the KRC display is tapped. The two additional functions get_mouse_middle_button and get_mouse_right_button are also available but have no meaning on the KRC (unless a mouse is attached)

Categorized Function List (cross-linked)

Each function listed is provided with a cross link to its library description.

Botball

  • void shut_down_in(double delay); [§]
  • void wait_for_light(int light_port_); [§]

Graphics

  • int graphics_open(int width, int height); [§]
  • void graphics_close(); [§]
  • void graphics_update(); [§]
  • void graphics_clear(); [§]
  • void graphics_fill(int r, int g, int b); [§]
  • void graphics_pixel(int x, int y, int r, int g, int b); [§]
  • void graphics_line(int x1, int y1, int x2, int y2, int r, int g, int b); [§]
  • void graphics_circle(int cx, int cy, int radius, int r, int g, int b); [§]
  • void graphics_circle_fill(int cx, int cy, int radius, int r, int g, int b); [§]
  • void graphics_rectangle(int x1, int y1, int x2, int y2, int r, int g, int b); [§]
  • void graphics_rectangle_fill(int x1, int y1, int x2, int y2, int r, int g, int b); [§]
  • void graphics_triangle(int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b); [§]
  • void graphics_triangle_fill(int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b); [§]
  • int get_mouse_position(int *x, int *y); [§]
  • int get_mouse_left_button(); [§]

Math

  • double atan(double angle); [§]
  • double cos(double angle); [§]
  • double exp(double num); [§]
  • double exp10(double num); [§]
  • double log(double num); [§]
  • int log10(double num); [§]
  • double pow(double x, double y); [§]
  • int rand(int m); [§]
  • double sin(double angle); [§]
  • double sqrt(double num); [§]
  • void srand(int s); [§]
  • double tan(double angle); [§]

Motors

  • void alloff(); [§]
  • void ao(); [§]
  • void bk(int m); [§]
  • void block_motor_done(int m); [§]
  • void bmd(int m); [§]
  • int get_motor_done(int m); [§]
  • void clear_motor_position_counter(int motor_nbr); [§]
  • void fd(int m); [§]
  • int get_motor_done(int m); [§]
  • int get_motor_position_counter(int m); [§]
  • void mav(int m, int vel); [§]
  • void motor(int m, int p); [§]
  • void move_at_velocity(int m, int vel); [§]
  • void move_relative_position(int m, int speed, int pos); [§]
  • void move_to_position(int m, int speed, int pos); [§]
  • void mrp(int m, int vel, int pos); [§]
  • void mtp(int m, int vel, int pos); [§]
  • void off(int m); [§]

Output

  • void beep(); [§]
  • void console_clear(); [§]
  • void display_clear(); [§]
  • void display_printf(int col, int row, char s[], . . .); [§]
  • void extra_buttons_show(); [§]
  • void extra_buttons_hide(); [§]
  • void set_a_button_textchar txt[]); [§]
  • void set_b_button_textchar txt[]); [§]
  • void set_c_button_textchar txt[]); [§]
  • void set_digital_output(int port, int inout); [§]
  • void set_digital_value(int port, int value); [§]
  • void set_x_button_textchar txt[]); [§]
  • void set_y_button_textchar txt[]); [§]
  • void set_z_button_textchar txt[]); [§]

Threads

  • thread thread_create(<function_name>); [§]
  • void thread_destroy(<thread id>); [§]
  • void thread_start(<thread id>); [§]
  • void thread_wait(<thread id>); [§]

Sensors

  • int a_button(); [§]
  • int a_button_clicked(); [§]
  • int accel_x(); [§]
  • int accel_y(); [§]
  • int accel_z(); [§]
  • int analog(int p); [§]
  • int analog8(int p); [§]
  • int analog10(int p); [§]
  • int any_button(); [§]
  • int b_button(); [§]
  • int b_button_clicked(); [§]
  • int c_button(); [§]
  • int c_button_clicked(); [§]
  • int digital(int p); [§]
  • double power_level(); [§]
  • int get_extra_buttons_visible(); [§]
  • int side_button(); [§]
  • int side_button_clicked(); [§]
  • int x_button(); [§]
  • int x_button_clicked(); [§]
  • int y_button(); [§]
  • int y_button_clicked(); [§]
  • int z_button(); [§]
  • int z_button_clicked(); [§]

Servos

  • void disable_servo(int p); [§]
  • void disable_servos(); [§]
  • void enable_servo(int p); [§]
  • void enable_servos(); [§]
  • int get_servo_enabled(int srv); [§]
  • int get_servo_position(int srv); [§]
  • int set_servo_position(int srv, int pos); [§]

Time

  • void msleep(int msec); [§]
  • double seconds(); [§]

Vision

  • void camera_close(); [§]
  • int camera_load_config(char name[]); [§]
  • int camera_open(); [§]
  • int camera_update(); [§]
  • int get_channel_count(); [§]
  • int get_object_area(int channel, int object); [§]
  • rectangle get_object_bbox(int channel, int object); [§]
  • point2 get_object_center(int channel, int object); [§]
  • point2 get_object_centroid(int channel, int object); [§]
  • double get_object_confidence(int channel, int object); [§]
  • int get_object_count(int channel); [§]
  • char *get_object_data(int channel, int object); [§]
  • int get_object_data_length(int channel, int object); [§]

Botball

Functions are included in the KIPR Library to assist programmers in writing programs that meet the basic requirements for a Botball game:

  • wait_for_light(<port_num>), a function for ensuring the robot doesn't begin operating until the starting light comes on.
  • shut_down_in(<time>), a function which ensures the robot stops operating within the time limits for a Botball game.

For the typical Botball program, the first executable statement will be

wait_for_light(<port_num>);
light sensor plugged into port port-num

If there are any other set up routines being used, then usually wait_for_light will immediately follow them. wait_for_light takes the robot operator through a process of using the starting light to set calibration values for the IR light sensor plugged into the specified port. The purpose is to set two values, one to check the sensor reading with light off and the other with light on. The difference between the two values has to be large enough for the sensor to determine if the light is off or on. If the sensor is suitably shielded and is positioned so that a successful calibration is achieved, wait_for_light calls for "hands off" and blocks further execution until the light is turned on. When "light on" is detected by the sensor, the program resumes and runs the robot according to program design. If problems occur in the calibration process, the user is notified and the process repeats after a brief pause.

The first statement in a Botball programm following wait_for_light is usually a call to the Botball function

shut_down_in(<time>);

which will shutdown all motors once the specified time has expired, then terminate the program.

The library functions for Botball are:


shut_down_in(<time>);
Starts a timing routine that will end program execution, and turn off all motors when the specified amount of time has elapsed.
wait_for_light(<port>);
Steps the user through a calibration procedure to establish on/off light levels for a sensor plugged in the specified port. If calibration does not provide enough discrimination between light off and light on, the program cycles for another try. If there is enough discrimination, after "light off", the program blocks continued execution of the function until "light on", at which point execution resumes. wait_for_light is normally placed at the beginning of a program designed to operate a robot, so that after calibration, when the light comes on the robot will start autonomously.

Example program for the KRC using a Botball program format


#include <kipr/botball.h>
int main()
{
   double s;
   int i;
   /* Botball calibration: determine if light sensor can discriminate between light and dark */
   wait_for_light(3);  // light sensor in analog port 3
   /* Botball timing: limit is 120 seconds; e.g., shut_down_in(119.5); */
   shut_down_in(10.5); // stop execution once 10.5 seconds have elapsed
   /* Botball program logic would be next ... the stuff below is just to keep the program running for awhile */
   display_clear(); // clear display for display_printf
   s=seconds(); // system on time at start of run
   for(i=0; i<15; i++) {
      display_printf(1,1,"%d. time elapsed = %.2f     ", i, seconds()-s);
      msleep(1000); // sleep for a second
   }
   display_printf(1,3,"done");
   return 0;
}

Threads

The term thread is short for the phrase "thread of execution", and represents a sequence of instructions to be managed by the system as it schedules processing time among running processes. On a single processor machine, like the KRC, the instructions running in separate threads appear to be operating in parallel. Each thread, once started, will continue until its process finishes or until it is forcibly terminated by another process using the thread_destroy function. Each active thread gets a small amount of time in turn until all of its statements have been executed or it is forcibly terminated. If a thread's process cannot complete all of its statements before its turn is over, it is paused temporarily for the next thread to gets its share of time. This continues until all the active threads have gotten their slice of time and then thread processing repeats. The KRC's processor is fast enough so that from the user's viewpoint it appears that all of the active processes are running in parallel.

Functions running in threads can communicate with one another by reading and modifying global variables. The global variables can be used as semaphores so that one process can signal another when it is not in a section of code that might cause a conflict. Process IDs may also be stored in global variables of type thread so that one process can destroy another one's thread if that is necessary program logic (think in terms of a process that is in an indefinite loop monitoring sensors, so it will never finish otherwise).

Since the operating system limits how many threads can be created, it is inadvisable to create threads in a loop. Good thread management is to destroy a thread rather than leaving it hanging around once it is no longer being used. Threads not destroyed by program end may still count against the operating system limit when the program is run again (unless of course the system has been rebooted). This may result in a program which for no observable reason ceases to work properly. It is up to the program to keep track of a thread it creates, and there are no means (short of rebooting) to destroy a thread after the fact.

The library functions for controlling threads are:


thread_create(<function_name>);
Creates a thread for running the specified function and returns a value of type thread, which is the thread ID to be used for running the thread as an independent process.
thread_destroy(<thread_id>);
Deactivates the specified thread if it is active (stopping its associated function) and destroys its thread ID.
thread_start(<thread_id>);
Activates the specified thread by running its associated process in the thread.
thread_wait(<thread_id>);
Suspends execution of the function that calls thread_wait while the specified thread remains active. It's purpose is to synchronize threads by suspending further execution of a function until the selected active threads have finished.

Example program for the KRC using threads

This example illustrates how to detect a button press even it occurs while the main process is paused.


#include <kipr/botball.h>
int flag=0; // global flag to signal when side button pressed
void chksens() {
   while (1) {
      if (side_button()) flag = 1;
      msleep(100); // check side button every 1/10th second
   }
}
int main() {
   int cnt = 0;
   thread tid; // thread variable for holding thread id
   tid = thread_create(chksens); // create a thread for chksens
   thread_start(tid); // start chksens running in its thread
   while (flag == 0) { // button press during sleep is still caught
      display_printf(1,2,"elapsed time %d   ",++cnt);
      msleep(1000);
   }
   thread_destroy(tid); // remove the thread
   display_printf(0,4,"done");
   return 0;
}