// THE COMPLETE GUIDE

FANUC Robot Programming

Whether you're programming your first FANUC robot or tightening up your approach — here's how I think about it after 18 years.

GUIDE TP KAREL BY JAY STRYBIS · UPDATED 7/2/2026 · ~25 MIN READ

I remember being nervous while making changes to a production cell when I first started working at FANUC. Will this crash the robot? Will this brick the controller?

I mostly learned through experiencing pain. One of my first tasks in 2008 was to put together a demo of an M-420iB case-packing some random boxes coming in on a conveyor. Vision and line tracking — not the easiest first project.

I think this was the first time I ever saw the TP programming language syntax, and I was immediately struck by how easy it was to read. The language is simple and hasn't changed much in decades.

I'm sure I crashed the robot a couple of times while working on that cell. I definitely remember breaking some sort of pressure gauge off of an M-410 during the PalletTool training class — you have to watch everything while moving the robot. But there was a saying at the time:

"If you've never crashed a robot, you've never driven a robot."

I'd been programming since I was a little kid, so the case-packing program showed me some pain immediately. Do I really need to teach a unique point for each of these drops? Does TP have arrays or something? There must be a better way.

I asked around, and sure enough, someone showed me the Position Register screen. Not exactly what I was looking for, but it would definitely help. With Position Registers I could teach one point and generate subsequent positions relative to it. I could also use Frame Offsets and Tool Offsets to get a nice motion profile without repeating myself.

My point is this: you can't fully understand what programming a FANUC robot involves until you do it. You need someone to show you the basics and then let you loose on a project. Use your brain, try your best, experience some pain — and then see if there's a better way.

That's what I tried to do with my book, Robot Whispering. We start with the basics, then build up a simple application from scratch, sometimes doing things the hard way before refactoring into something better. I think that's the best way to really learn how and why to do things a certain way.

01

A mental model for structure & motion

Program architecture and efficient motion are probably the two hardest concepts for new robot programmers to get. Here's how I think about both.

99% of the time I start with a main program where the cell controller (PLC or PC) calls the shots. The shots are high-level tasks the robot must perform: pick a part, place a part, go home, go to maintenance. From there, the structure of any given task is basically this:

THE RECIPE FOR MOST TASKS
02Make sure my gripper is ready (open + empty, or closed + full)
03Wait for the station to be ready / request access
04Move to the operation position
05Do the thing (pick, drop, etc.)
07Retreat
10Exit the station and say we're clear

That's it. There are finer details — there's probably a lot of logic inside "move safely to the target station" — but that's the recipe for most tasks.

For efficient motion, I always think about getting from A to B with as few points as possible, as fast as possible, using CNT100 as much as possible. Joint moves are ideal if you can swing it. Your robot can easily get wrapped up with lots of linear moves and never using a joint move to force the correct joint configuration.

02

TP vs. KAREL

FANUC robots are programmed in two languages: TP (the teach pendant language) and KAREL. 99% of the time you're going to be writing TP.

TP
99% OF YOUR WORK
Motion. Station logic. I/O. Everything that actually moves the robot. Simple, readable, and essentially unchanged for decades.
KAREL
WHEN TP CAN'T
File I/O, socket messaging, position transformations, monitoring, custom UIs via the form manager, complex multi-tasking. No motion — FANUC removed that long ago.

KAREL is used for the more complex tasks TP cannot do: reading and writing files (see also writing a logging utility), socket messaging, position transformations, monitoring, custom user interfaces with the FANUC form manager, complex multi-tasking, and so on.

For an experienced programmer, KAREL is nice because it gives you typed variables, custom data structures, routines with variables passed by reference, return values. It's easy to test — particularly because we're generally not doing any motion with KAREL. You'll have to shell out to a TP program if you want to move the robot.

03

The teach pendant

I love the teach pendant and the FANUC operating system. Maybe it's just because I've been using it so long, but I honestly think it's pretty easy to use. Almost everything is one shortcut key plus F1 [TYPE] away.

SHORTCUTS I USE EVERY DAY
SELECTPrograms. Filter by F1, select with ENTER — your editor is right there.
DATANumeric Registers, Position Registers, and friends.
STATUSEvery available status screen.
I/OPick your I/O type and go. Pretty intuitive.
SETUPLots of good stuff: Frames, Host Comm, BG Logic, User Alarms.
MENUThe start menu. Everything is in the tree if you know where to look.

Alarms (see how to diagnose FANUC alarm and error codes), File and System are probably the only other top-level items I use day to day.

It takes a little time to get comfortable with where everything is, but you generally get there via the shortcut or MENU, then use F1–F5 (and maybe Prev/Next) to operate the screen.

04

Running and controlling programs

As I mentioned, 99% of the time some cell controller (likely a PLC) is in charge. I wrote a long article on starting FANUC robots in auto that pretty much covers everything.

For controlling the program, the main things are the UOP signals and controlling the robot's speed override. Generally you'll map I/O between the PLC and the robot and either use a Group Input to pass a task number, or just a DI if that's easier.

Your MAIN program structure is then something like this:

MAIN.TPTP
LBL[1] ;
CALL GET_TASK_ID_FROM_PLC ;
SELECT R[1:TaskID]=1,CALL TASK_001 ;
       =2,CALL TASK_002 ;
       =3,CALL TASK_003 ;
       ELSE,JMP LBL[501] ;
JMP LBL[1] ;

Where GET_TASK_ID_FROM_PLC might be something like:

GET_TASK_ID_FROM_PLC.TPTP
LBL[1] ;
WAIT (GI[1:TaskID]<>0) TIMEOUT,LBL[501] ;
R[1:TaskID]=GI[1:TaskID] ;
GO[1:TaskID]=R[1:TaskID] ;
WAIT (GI[1:TaskID]=0) TIMEOUT,LBL[502] ;
END ;
 ;
LBL[501] ;
! timeout waiting for task ;
! TODO: alarm? ;
JMP LBL[1] ;
 ;
LBL[502] ;
! timeout waiting for task ack ;
! TODO: alarm? ;
JMP LBL[1] ;

Or if you're just using DIs:

GET_TASK_ID_FROM_PLC.TP · DI VARIANTTP
LBL[1] ;
R[1:TaskID]=0 ;
IF (DI[1:Run Task001]),R[1:TaskID]=(1) ;
IF R[1:TaskID]<>0,JMP LBL[999] ;
 ;
IF (DI[2:Run Task002]),R[1:TaskID]=(2) ;
IF R[1:TaskID]<>0,JMP LBL[999] ;
 ;
IF (DI[3:Run Task003]),R[1:TaskID]=(3) ;
IF R[1:TaskID]<>0,JMP LBL[999] ;
 ;
! no task given ;
! maybe add a short wait ;
WAIT 0.01(sec) ;
JMP LBL[1] ;
 ;
LBL[999] ;
END ;

You basically want to write clean code that's easy to maintain.

05

Working with variables and data

Writing complex TP programs isn't a fun time. Most of the data you'll use is global — Numeric Registers, Position Registers — and because these are referenced by index instead of name, you'll have to map out this memory by hand in a spreadsheet.

▸ NOTE
You can use Fexcel to automatically label your data from Excel — and even use names in your programs instead of raw indices.

For anyone used to declaring named local variables or passing values to functions, this is a bit of a rude awakening. I hated it so much that I wrote my own programming language.

2014
A fun project that taught me how compilers work, but cumbersome in production — keeping high-level source in sync with pendant edits was hard, and I never solved it. Matt at Group Six Technologies took it over and still maintains it.
2021
Closer to the metal: the source is line-for-line the same, but registers, position registers and I/O are replaced by names defined in your spreadsheet. Great when I remember to use it — same sync problem, though.
NEXT
LS → Fexcel
I've written tooling to compare local binaries against what's on the robot. The next step is converting a standard FANUC LS file into a Fexcel file so I don't have to. Stay tuned.
06

Testing and best practices

Testing TP programs

Projects go way more smoothly when you get them 99% of the way there in ROBOGUIDE first. Trying to figure out your motion on a real robot is painful. Do it in ROBOGUIDE first — trust me.

That means getting CAD from your mechanical engineers. ROBOGUIDE tends to choke on large files, so simplify if possible and send different stations separately. A good trick when exporting from SolidWORKS is to define a new coordinate system — perhaps one at the robot origin — and use that when exporting your IGES or STL files.

When exporting part and gripper CAD, make sure the coordinate system makes sense. Teach your mechanical engineers how the robot faceplate coordinate system works (+Z out, +X up when at zero) so you don't have to painfully move CAD around after importing.

ROBOGUIDE v10 is a little better at importing CAD, but as of 7/2/2026 I've found v10 Classic to be more stable for v9.40 projects.

A lot of times I'll have a register or flag bit in my TP programs to bypass certain programs or checks while running in ROBOGUIDE — e.g. IF (R[x:SIM]=1),JMP LBL[y]. It can be painful to manually toggle I/O all the time to get through your routines.

The two biggest pieces to get figured out in ROBOGUIDE are:

01
Motion & wrist orientation
Your large station-to-station moves and eventual wrist orientation at each station. Are you running NUT or FUT? J4/J6 turn counts 0, −1 or 1?
02
DCS
Make sure your mechanical engineers gave you enough room to keep the robot, EOAT and part safely away from the fence.

If the NUT/FUT or turn-count stuff is new, read The Perils of Six Axis Robots.

For DCS, you'll have to do your own safety analysis, but a good rule of thumb for the gap between DCS and your fence:

PAYLOADRULE-OF-THUMB GAP
< 10 kg50 mm
< 150 kg150 mm
< 300 kg500 mm
300 kg +700 mm +
⚠ NOT SAFETY ADVICE
I'm not a safety expert. Take these values with a grain of salt and do your own safety analysis. And mechanical engineers: just because the rule of thumb is 150 mm doesn't mean give the robot guys exactly 150 mm. The robot needs to get in and out of stations, and DCS user models don't match CAD exactly. Leave some room.

It's best to verify there's a big enough gap early in the project, so the engineers can adjust the layout or guarding if necessary.

Once the big motion decisions and DCS are out of the way, get a head start on homing. I like to use the exact same retreat routines for my main high-speed production routines as I do for homing — that way I know these routines have a lot of runtime on them and don't only run once in a blue moon. I also don't have to write essentially the same logic twice.

To test homing, I run a routine and stop the robot at various points throughout the motion, then abort and run the homing routine. The robot should home just fine. If it faults or does something weird, fix the homing routine. When homing from a station, I write code assuming the robot is the furthest it can get into a station and write retreat code from there:

HOME_FROM_STATION.TPTP
CALL PROGRAM_TO_VERIFY_ROBOT_IS_AT_STATION ;
 ;
UFRAME_NUM=1 ;
UTOOL_NUM=1 ;
PR[x:LPOS]=LPOS ;
 ;
! assume robot is at pick position at this point ;
! and write a check to falsify that assumption ;
IF (PR[x,3:LPOS]>a),JMP LBL[1] ;
! robot is probably at pick — move to first retreat ;
PR[x,3:LPOS]=a ;
L PR[x] 500mm/sec CNTx ;
 ;
LBL[1] ;
! ok we are at least at Z=a ;
IF (PR[x,1:LPOS]<b),JMP LBL[2] ;
! need to move back to b ;
PR[x,1:LPOS]=b ;
L PR[x] 500mm/sec CNTx ;
 ;
LBL[2] ;
! ok we're at least Z=a and X=b... ;
J PR[y:perch] 100% CNT100 ;

Testing KAREL programs

As I mentioned, testing KAREL is easy to do. It's also pretty important, because you're probably doing some complex stuff.

It's not necessary, but I wrote KUnit back in 2014 to help with simple assertions while testing KAREL. I run a cygwin console environment and use curl to hit KUnit and run all my tests right from the console — make test.

If I have a KAREL library called lib, I'll have a program called test_lib.kl with a routine per routine I need to test:

test_lib.klKAREL
PROGRAM test_lib
...
ROUTINE test_add
VAR
    result : INTEGER
BEGIN
    kunit_test('1+1=2', kunit_eq_int(2, add(1,1)))
    kunit_test('2+2=4', kunit_eq_int(4, add(2,2)))
    kunit_test('4+1=5', kunit_eq_int(5, add(4,1)))
END test_add

BEGIN
    kunit_init
    test_add
    kunit_done
END test_lib
      
MakefileMAKE
HOST ?= 127.0.0.2

test:
    curl -m 5 http://$(HOST)/karel/kunit?filenames=test_lib
    curl -m 5 http://$(HOST)/karel/kunit?filenames=test_lib2
      

KUnit supports passing multiple filenames at once, but it runs those in parallel — which may not work if your code isn't threadsafe. I usually just test each file with its own HTTP call.

07

Backing up your robots

You can use a USB stick to back up your robots, but it's easier to use FTP. BackupTool can back up all your robots at the same time with one command.

08

Where to go deeper

This is really just the beginning of all there is to FANUC robot programming. If you want to go deeper, I wrote a book that covers everything I think someone needs to know to get up and running and productive on FANUC robots.

Robot Whispering
THE BOOK
Robot Whispering
The unofficial guide to programming FANUC robots — start to finish.
Get the book →
// (ALMOST) EVERY TUESDAY

There's more where that came from.

I email (almost) every Tuesday with the latest insights, tools and techniques for programming FANUC robots.