Introduction to KAREL Programming

Filed under: FANUC KAREL Programming

You can do just about anything with FANUC’s TP programming language, but there are some things it just can’t do. Enter KAREL.

KAREL is a lower-level language very similar to Pascal. It features strongly typed variables, constants, custom types, procedures, functions, and gives you access to all sorts of useful built-ins for things you may not be able to do with TP.

KAREL is a compiled language; the source must be translated from a KAREL source file (.KL) into p-code (.PC) before it can be loaded and executed on the controller. Once your KAREL program has been loaded onto the controller, it acts like a black box; you can’t see the source or step through it like a TP program.

As of the R-30iB controller your robot must have the KAREL software option in order to load your own custom KAREL programs.

Hello, world

We all know that outputting “Hello, world” is absolutely vital to learning any new programming language. Let’s make it happen in KAREL:


-- hello_world.kl
PROGRAM hello_world
BEGIN
  WRITE('Hello, world!',CR)
END hello_world

Program Structure

Let’s take a quick look at this program. The PROGRAM statement followed by the name of the program must be the first statement in any KAREL program. The END statement must use the same identifier.

Any statements within the BEGIN and END statements are executed by the KAREL program. You can probably guess what the WRITE statement does. You can optionally provide it with a FILE to write to, but by default it will write to the default TPDISPLAY, the USER screen. Each item you want to write can be separated by commas, and the CR stands for ‘carriage return.’ Any subsequent WRITE statements will take place on the next line.

Translating

You can either edit and translate your KAREL source files directly in ROBOGUIDE or use the ktrans command line utility that comes with it.


> ktrans hello_world.kl

Running your program

You can test your program in ROBOGUIDE or on a real controller. Just transfer the .PC file to the controller via your favorite method (FTP, USB stick, PCMCIA card, etc.), then run it or CALL it from a TP program. If you go to the USER screen (Menu > 9), you should see “Hello, world!” on the screen.

Details

Here’s how a KAREL program is structured:

  • PROGRAM statement
    • Translator directives (optional)
    • CONST, TYPE, and/or VAR declarations (optional)
    • ROUTINE declarations (optional)
    • BEGIN
      • Executable statements (optional)
    • END statement
      • ROUTINE declarations (optional)

Let’s create a quick program that demonstrates all of these options.


-- hello.kl
PROGRAM hello
  -- this is a comment

  -- translator directive
  %COMMENT = 'Hello'

  -- const declarations
  CONST
    LANG_EN = 1
    LANG_ES = 2
    LANG_FR = 3

  -- custom type
  TYPE
    person_t = STRUCTURE
      name     : STRING[16]
      lang_id  : INTEGER
    ENDSTRUCTURE

  -- program vars
  VAR
    people : ARRAY[3] OF person_t

  -- custom routines
  -- Returns the proper greeting for a given language
  ROUTINE greeting(lang_id : INTEGER) : STRING
    BEGIN
      SELECT lang_id OF
        CASE(LANG_EN):
          RETURN('Hello')
        CASE(LANG_ES):
          RETURN('Hola')
        CASE(LANG_FR):
          RETURN('Bonjour')
      ENDSELECT
    END greeting

  -- Greets a person in their language
  --
  -- Example:
  --   person.name = 'John'
  --   person.lang_id = LANG_EN
  --
  --   greet(person)
  --   # => Hello, John
  ROUTINE greet(person : person_t)
    BEGIN
      WRITE(greeting(person.lang_id),', ',person.name,CR)
    END greet
BEGIN
  -- setup people[] array
  -- notice KAREL arrays are 1-based, not 0-based.
  people[1].name = 'John'
  people[1].lang_id = LANG_EN

  people[2].name = 'Jose'
  people[2].lang_id = LANG_ES

  people[3].name = 'Jaques'
  people[3].lang_id = LANG_FR

  -- greet each person
  greet(people[1])
  greet(people[2])
  greet(people[3])
END hello
  -- you could also put your routines here

There’s a lot going on in this program, so let’s go through it section by section.

Translator Directives

The %COMMENT directive changes the comment that displays on the right of the teach pendant SELECT menu.

CONST Declarations

Constants are values that cannot be changed in the program. I’ve created a unique ID for each language that I know won’t be changed.

TYPE Declarations

I created a custom type for a person. Each person data structure can have a name of type STRING[16] and a language_id of type INTEGER. You can access elements of your custom type with “dot” syntax (e.g. type.element).

VAR Declarations

I defined a variable named people that is an array of up to 3 person_t data structures.

ROUTINE Declarations

I defined two routines: greeting, a FUNCTION which returns a STRING value, and the PROCEDURE greet, which just WRITEs a message to the screen.

Executable Statements

Since I’ve created a 3-slot array of people, I have to initialize those values. (Alternatively, you could set the KAREL VARs directly from the DATA screen, but this is more demonstrative for this contrived example.) I then call the greet() PROCEDURE, which takes a person_t as an argument.

The output should be:


Hello, John.
Hola, Jose.
Bonjour, Jaques.

Gotchas

KAREL is an old language, so it has some pretty nasty limitations that are remnants of an era when memory was hard to come by.

Character Limits

Identifiers (program name, constant names, variable names, routine names, etc.) are all limited to 12 characters. Naming is hard. It’s even harder when you have to smash things into 12 characters.

Program Length Limits

If your program gets too long, you’ll have to split it up into multiple programs.

Variable Clashes

You may run into issues if you change a variable’s type or do something like expanding an array, etc. If you run into any errors like this, you’ll have to delete both the .PC and .VR file for your program before re-loading it.

Cumbersome API

Many of the built-in procedures aren’t exactly easy or intuitive to use. Take for example, the GET_VAR built-in for getting a variable:


GET_VAR(entry, prog_name, var_name, value, status)

Personally, I like to use assignment over calling procedures. I’d generally write some general-purpose ROUTINEs to simplify things e.g.


ROUTINE get_int(prog_name : STRING[16]; var_name : STRING[16]) : INTEGER
  VAR
    entry  : INTEGER
    i      : INTEGER
    status : INTEGER
  BEGIN
    GET_VAR(entry, prog_name, var_name, i, status0
    IF status<>0 THEN
      WRITE('Could not get int [',prog_name,']',var_name,'status:',status,CR)
    ENDIF

    RETURN(i)
  END get_int


-- which option looks better?
-- option 1:
GET_VAR(entry,'test_prog','test_int',my_int,status)
IF status<>0 THEN
  WRITE('Could not get [test_prog]test_int status:',status,CR)
ENDIF

-- option 2:
my_int = get_int('test_prog','test_int')

Lack of Support Built-Ins

While KAREL does provide quite a few built-in procedures, it’s missing many common features you might expect from a programming language for things like Arrays, Strings, etc. You may find yourself implementing rudimentary methods over and over again before developing your own environment libraries.

Conclusion

This is just the tip of the KAREL iceberg. Hopefully this example will get you thinking how KAREL may be the right choice for certain programming tasks where TP may not be so good. If you really want to learn KAREL, see if you can get a copy of the KAREL manual from your local FANUC rep or integrator.

More on KAREL

I’ve written several other articles on FANUC KAREL programming. If you are already familiar with KAREL or you want to look at some more programming examples, check out KUnit, an open-source unit testing framework for FANUC KAREL. If you want to learn more about unit testing, read this article about testing FANUC TP and KAREL code.


Want posts like this delivered right to your inbox?

If you liked this post, please sign up for my mailing list!