All writing
Software & Data3 min read

How to read FANUC machine data with FOCAS and Python

Machine-monitoring vendors charge per-machine to show you numbers your control already knows. FOCAS is the same API they use — and you can call it in a few lines of Python.

Inside a FANUC Robodrill machining centre — tool carousel and spindle

Photo: Mixabest · CC BY-SA 3.0

Every machine-monitoring product on the market — the ones that charge you a monthly fee per machine — is, at its core, reading a handful of numbers off your control: is it running, what's the feedrate, what's the spindle load, which program is active, is there an alarm. On a FANUC control, they read those numbers through FOCAS. And FOCAS is not a secret. You can call the exact same API yourself.

FOCAS (FANUC Open CNC API Specifications) is FANUC's official library for talking to a control from a PC. It ships as a set of .dll files, and every function you'll ever need — read a macro variable, read the active program, read an alarm — is one call away once you're connected.

You need the library, and the machine needs the option

The Windows library (Fwlib32.dll) comes from FANUC, and the control usually needs the Data Server or FOCAS/Ethernet option enabled to accept a connection. That's the one gate: no option, no data. Check before you promise anyone a dashboard.

What you actually need

  • A FANUC control on the network — 0i, 30i/31i/32i, and most modern models, with FOCAS/Ethernet enabled.
  • The Fwlib library — FANUC's Fwlib32.dll (Windows). The open-source fwlib project helps with headers and compiling.
  • A language binding — call the DLL from Python with ctypes, or use a wrapper so you don't hand-roll every struct.

The three-line pattern every FOCAS program follows

Strip away the details and every FOCAS program is the same shape: open a handle to the control, read what you want, free the handle. Here's that skeleton in Python calling the DLL directly with ctypes:

import ctypes

fwlib = ctypes.windll.Fwlib32   # FANUC's licensed DLL

# 1. connect: IP, port 8193, 10s timeout -> a handle
handle = ctypes.c_ushort()
ret = fwlib.cnc_allclibhndl3(b'192.168.0.10', 8193, 10, ctypes.byref(handle))
if ret != 0:
    raise RuntimeError(f'connect failed, code {ret}')

# 2. read: actual feedrate + spindle speed into an ODBSPEED struct
#    (the struct layout comes from fwlib.h / your wrapper)
speed = SpeedData()
fwlib.cnc_rdspeed(handle, -1, ctypes.byref(speed))
print('feed:', speed.actf.data, ' rpm:', speed.acts.data)

# 3. always free the handle
fwlib.cnc_freelibhndl(handle)

cnc_allclibhndl3 opens an Ethernet handle to the control. Every subsequent call takes that handle. The annoying part isn't the calls — it's the C structs each function writes into (ODBSPEED, ODBACT, ODBALMMSG…). That struct plumbing is exactly why wrappers exist.

The EW_ trap

When a call returns a non-zero code like EW_FUNC or EW_OPTION, it usually doesn't mean your code is wrong — it means the machine doesn't have that function's option enabled. Half of learning FOCAS is learning to read those return codes instead of assuming a bug.

The numbers most people actually want

  • Feedrate & spindle speedcnc_rdspeed (or cnc_actf / cnc_acts).
  • Spindle & servo loadcnc_rdspload / cnc_rdsvmeter, the basis of tool-wear and health signals.
  • Active program & blockcnc_rdexecprog / cnc_rdprgnum, so you know what is running.
  • Run statecnc_statinfo tells you running / hold / stop.
  • Alarmscnc_rdalmmsg returns the alarm text, the single most useful thing to log.
  • Macro variablescnc_rdmacro reads probing results, part counts, and tool data (its own full post).

Python, or C#?

Three roads, and they're all valid. `ctypes` + Fwlib in Python is the quickest to prototype. [pyfanuc](https://github.com/diohpix/pyfanuc) reverse-engineers the protocol so you don't need the FANUC DLL at all — which means it runs on Linux, a real advantage for a Raspberry Pi collector. And the [Ladder99 fanuc-driver](https://github.com/Ladder99/fanuc-driver) (C#/.NET) is a configurable collector that post-processes straight to MQTT if you'd rather not write the loop yourself.

The gap between "we should monitor our machines" and "we are monitoring our machines" is about forty lines of code — most people just don't know the forty lines exist.

Once you can read these numbers on a timer, everything downstream opens up: a live dashboard, an OEE calculation, tool-wear trending, even an AI agent that answers questions about your machines. If you'd rather have someone build the collector than fight struct definitions, get in touch — this is exactly the kind of thing I do.

Muerus Rodrigues

Applications Engineer

Get in touch

Keep reading

Home
Blog
Email
LinkedIn
Résumé