LinuxCNC Documentation
This page is 7% translated. Untranslated text is shown in English.

This documentation describes the hal Python module, which provides a Python API for creating and accessing HAL pins and signals.

Important

The classes inside the Python module hal are layered on top of the module implementation called _hal. Many features described in this document are only available in the hal Python module.

You should only import and access the hal module API as described in this document. The base-class methods, members, properties and values may change without notice.

1. Basic usage

Simple example creating component and pins
#!/usr/bin/env python3
import hal
import time

comp = hal.component("multiply")
comp.newpin("in-a", hal.Type.REAL, hal.Dir.IN)
comp.newpin("in-b", hal.Type.REAL, hal.Dir.IN)
comp.newpin("out", hal.Type.REAL, hal.Dir.OUT)
comp.ready()

try:
    while True:
        comp['out'] = comp['in-a'] * comp['in-b']
        time.sleep(0.001)
except KeyboardInterrupt:
    raise SystemExit

2. Class hal

2.1. hal enumerations and constants

There are many constants used to indicate type, direction, levels and much more. These are usually integer values with a symbolic name. It is preferable in code to use the symbolic name for readability and portability.

Enumerations have been added to aid the readability even more. They use the Python IntEnum base class. The IntEnum values behave as numerical values, but add clear visibility features for readability. The old defined constants are still supported for backwards compatibility.

Example:
# Old (deprecated) style:
comp.newpin("in-a", hal.HAL_FLOAT, hal.HAL_IN)

# New style:
comp.newpin("in-a", hal.Type.REAL, hal.Dir.IN)

2.1.1. HAL types

The hal.Type enum is used to specify the type of pin, parameter and signal:

  • hal.Type.BOOL - A boolean using True and False

  • hal.Type.SINT - A signed quantity with range -263…​+263-1

  • hal.Type.UINT - An unsigned quantity with range 0…​+264-1

  • hal.Type.REAL - A floating point quantity of range ±1.80×10308

  • hal.Type.PORT - An opaque quantity representing a communication channel (pins only)

  • hal.Type.S32 - A signed quantity with range -231…​+231-1 (see notes below)

  • hal.Type.U32 - An unsigned quantity with range 0…​+232-1 (see notes below)

There are aliases in the hal.Type enum for all types with the HAL_ prefix (e.g. hal.Type.HAL_BOOL, etc.).

Note

The constants previously used (hal.HAL_BIT, hal.HAL_FLOAT, etc.) are still available. You should upgrade your code to use the enumerated types with the new names instead.

Important

The 32-bit types will soon be replaced by 64-bit types. The names will remain for some time, but they will map to the larger type automatically. The S32 and U32 names will then no longer be unique and the range will be larger.

2.1.2. HAL direction

Just like types, hal.Dir enum is the enumeration to specify direction of pins and parameters. The direction for both pin and parameter direction is unified in one enumerated type:

  • hal.Dir.IN - An input pin

  • hal.Dir.OUT - An output pin

  • hal.Dir.IO - A bidirections pin

  • hal.Dir.RW - A read/write parameter

  • hal.Dir.RO - A read-only parameter

Also the direction enums are available with the HAL_ prefix (like hal.Dir.HAL_IN).

2.1.3. Old style HAL constants (deprecated)

Old style constants (deprecated): hal.HAL_BIT, hal.HAL_S32, hal.HAL_U32, hal.HAL_S64, hal.HAL_U64, hal.HAL_FLOAT, hal.HAL_PORT.

Old style deprecated pin direction constants (deprecated): hal.HAL_IN, hal.HAL_OUT, hal.HAL_IO.

Old style deprecated parameter access constants (deprecated): hal.HAL_RO, hal.HAL_RW.

2.1.4. Message level constants

  • hal.MSG_NONE - No messages at all

  • hal.MSG_ERR - Only errors

  • hal.MSG_WARN - Both warnings and errors

  • hal.MSG_INFO - Both informational, warning and error messages

  • hal.MSG_DBG - Additionally include debugging information

  • hal.MSG_ALL - Print all messages encountered, disregarding level

2.1.5. Realtime type enumerations

The enumerated type hal.RTType is used in the hal.get_realtime_type() method.

  • hal.RTType.UNINITIALIZED - Real time module not running

  • hal.RTType.NONE - No realtime available

  • hal.RTType.UNKNOWN - Only used when LINUXCNC_FORCE_REALTIME=1 is set. Unknown, no PREEMPT_DYNAMIC but SCHED_FIFO is available. Not recommended.

  • hal.RTType.PREEMPT_DYNAMIC - Preempt dynamic: available in vanilla kernel. Only used when LINUXCNC_FORCE_REALTIME=1 is set. Not recommended.

  • hal.RTType.PREEMPT_RT - Preempt RT

  • hal.RTType.RTAI - RTAI kernel mode

  • hal.RTType.LXRT - LXRT, userspace implementation for RTAI

  • hal.RTType.XENOMAI - Xenomai 3

  • hal.RTType.XENOMAI_EVL - Xenomai 4 aka Xenomai EVL

The realtime enumerations are also available as constants under the hal.REALTIME_TYPE_xxx name where xxx is one of the above.

2.1.6. Component type enumeration

The enumerated type hal.CompType is used in hal.query.comp() and hal.query.comps().

  • hal.CompType.REALTIME - a realtime component

  • hal.CompType.USER - a user non-realtime component

  • hal.CompType.OTHER - (HAL internal value; should normally not be visible)

2.1.7. Other constants

System information:

  • hal.is_kernelspace - One (1) if RTAPI runs in the kernel, otherwise zero (0)

  • hal.is_userspace - Inverted hal.is_kernelspace

  • hal.kernel_version - A string specifying the real-time kernel version if hal.is_kernelspace is one. Otherwise it specifies "Not Available".

  • hal.is_rt - One (1) if the system runs in real-time, otherwise zero (0) DEPRECATED: Use lcnc_realtime.verify()

  • hal.is_sim - Inverted hal.is_rt DEPRECATED: Use lcnc_realtime.verify()

2.2. hal methods

hal.is_initialized() → bool

Returns a boolean to indicate whether hal is initialized. This methods should always return True because the HAL module is automatically initialized at import time.

Example:
import hal

assert hal.is_initialized(), "fatal: HAL failed to initialize at import"
hal.get_realtime_type() → hal.RTType

Returns the type of the running realtime system. Might return hal.RTType.UNINITIALIZED if rtapi_app is not running. See realtime type constants. See also LinuxCNC Realtime check.

hal.component_exists(name:string) → bool

Returns a boolean to indicate whether or not the specified component exist at this time.

hal.component_is_ready(name:string) → bool

Returns a boolean to indicate whether or not the specified component is in the ready state. Also returns False if the component does not exist.

Example:
if not hal.component_is_ready("testpanel"):
    compmsg = "ready" if hal.component_exists("testpanel") else "loaded"
    print("Expected component 'testpanel' to be {}".format(compmsg))
    os.exit(1)
hal.set_msg_level(lvl:int)

Set the message level that controls the amount of information being printed and forwarded from real-time components. The lvl argument must be one of the message constants.

hal.get_msg_level() → int

Return the current message level. See hal.set_msg_level() and message constants for list of possible returned values.

hal.new_sig(name:string, type:enum) → bool

Create a new signal (net) called name. The signal can carry information of type content. Returns True on success.

Example:
if not hal.new_sig("signalname", hal.Type.BOOL):
    ...handle error...
hal.connect(pinname:string, signame:string) → bool

Connect the pin pinname to signal signame. Both signal and pin must exist and both pin and signal must be of the same type. Returns True on success.

Example:
if not hal.connect("mycomp.pinname", "signalname"):
    ...handle error...
hal.disconnect(pinname:string, signame:string) → bool

Disconnect the pin pinname from signal signame. Both signal and pin must exist. Returns True on success.

Example:
if not hal.disconnect("mycomp.pinname"):
    ...handle error...
hal.pin_has_writer(pinname:string) → bool

Returns True if pin with name pinname is attached to a signal and there is at least one writer. Otherwise, False is returned.

Example:
if hal.pin_has_writer("mycomp.0.pin.02"):
    print("Pin has writer")
else:
    print("Pin has no writer or no signal attached")
hal.set_p(name:string, value:mixed)

Sets the pin or param called name to value. The name is the full name of the pin or param. The search order is pin names first, then parameter names.
Throws a RuntimeError exception if the name is not found.
The type of value depends on the type of the pin or param. Integer scalar types may use integers or a textual representation of an integer to set the value. Floating point type may use both integer, floating point and textual representation thereof to set the value. Booleans accept True, False and map the integer value zero (0) to false. The exact floating point value of zero (0.0) also maps for false. Booleans may also be of text "0", "1", "on", "off", "true", "false", "yes" or "no". Textual representations are case insensitive.

Example:
hal.set_p("mycomp.0.bit", True)
hal.set_p("mycomp.0.float", 99.99)
hal.set_s(name:string, value:mixed)

Sets the signal (net) called name to value. The same rules for value apply to set_s() as to set_p().

The set_s() method has one special case when the signal is of type hal.Type.PORT and it is fully connected. In that case, the call uses the value to set the port’s queue size and it must be a positive integer. See below on configuring a port.

hal.get_p(name:string) → bool|int|float|None

Returns the value of the pin or param with name. If name refers to a pin and that pin is connected, then the connected signal’s value is returned. Boolean types return True or False. Integer scalar types return an integer. Floating point types return a float. The value None is returned if no pin or param is found by that name.

hal.get_s(name:string) → bool|int|float|None

Returns the value of the signal with name. Boolean types return True or False. Integer scalar types return an integer. Floating point types return a float. The value None is returned if no signal is found by that name.

hal.get_value(name:string) → bool|int|float

Returns the value of the pin, param or signal with name, searched in that order. Boolean types return True or False. Integer scalar types return an integer. Floating point types return a float. A RuntimeError exception is thrown if no pin, param or signal is found by that name.

Example:
value = hal.get_value("iocontrol.0.emc-enable-in")
hal.get_info_pins() → list(dict)

DEPRECATED: replaced by hal.query.pins().
Returns a list of dictionary tuples as in {"NAME":"pinname", "VALUE":<bool|int|float>, "TYPE":<int>, "DIRECTION":<int>}.

Example:
# Old style (deprecated):
for i in hal.get_info_pins():
    print(i['NAME'], i['TYPE'], i['DIRECTION'], i['VALUE'])

# New style (see hal.query sub-module below):
for pinname,pindetail in hal.query.pins().items():
    print(pinname, pindetail['type'], pindetail['dir'], pindetail['value'])
hal.get_info_params() → list(dict)

DEPRECATED: replaced by hal.query.params().
Returns a list of dictionary tuples as in {"NAME":"paramname", "VALUE":<bool|int|float>, "TYPE":<int>, "DIRECTION":<int>}.

hal.get_info_signals() → list(dict)

DEPRECATED: replaced by hal.query.signals().
Returns a list of dictionary tuples as in {"NAME":"signame", "VALUE":<bool|int|float>, "TYPE":<int>, "DRIVER":"name"|None}.

Example:
# New style (see hal.query sub-module below)
for signame,sigdetail in hal.query.signals().items():
    print(signame, sigdetail['type'], sigdetail['value'], sigdetail['driver'])

3. Class hal.component

3.1. component methods

comp = hal.component(name:string [, prefix:string])

The component itself is created by a call to the constructor hal.component. The arguments are the HAL component name and (optionally) the prefix used for pin and param names. If the prefix is not specified, the component name is used.

Example:
comp = hal.component("passthrough")
pin = comp.newpin(name:string, type:enum, io:enum)

Create new pin with actual name prefix.__name__. The pin type must be one of the types described above. The io specifies the direction of the pin. Throws a ValueError exception if name already exists.

Example:
p_in = comp.newpin("in", hal.Type.FLOAT, hal.Dir.IN)
param = comp.newparam(name:string, type:enum, access:enum)

Create new parameter with actual name prefix.__name__. The pin type argument must be one of the types described above. Parameters cannot be of type hal.Type.PORT. The access argument specifies the allowed param access. Throws a ValueError exception if name already exists.

Example:
p_bloop = comp.newparam("bloop", hal.Type.FLOAT, hal.Dir.RO)
comp.getitem(name:string) → object

Return the pin or param item object name previously created with comp.newpin() or comp.newparam(). Throws an AttributeError exception if no pin or param named name is found. Use comp.getpin() or comp.getparam() to find the specific type.

comp.getpin(pinname:string) → object

Return the pin item object pinname previously created with comp.newpin(). Throws an AttributeError exception if no pin names pinname is found. A param called pinname will not be found and throws an exception. Use comp.getitem() to find either.

comp.getparam(paramname:string) → object

Return the param item object paramname previously created with copm.newparam(). Throws an AttributeError exception if no pin names paramname is found. A pin called paramname will not be found and throws an exception. Use comp.getitem() to find either.

comp.getpins() → dict

Returns a dictionary all pin and param names and their values. The pin or param name is the dictionary key.

comp.ready()

Tells the HAL system the component is initialized. Locks out adding pins.

comp.unready()

Allows a component to add pins after ready() has been called. One should call ready() on the component when done.

comp.getprefix() → string

Returns the current component’s prefix used when creating pins and params. It defaults to the component name when not set in the constructor or by setprefix.

comp.setprefix(prefix:string)

Set the prefix used when creating pins and params. The prefix is used to create pins and params called __prefix__.name and defaults to the components name.

4. Class hal.stream

The streamer class enables sending typed value blocks, samples of data, between real-time and non-real-time. The values carried in a stream have the same types as pins and params. A stream can source data from pins or sink data into pins using ready made components. The stream carries values through a FIFO queue up to a specified depth. The stream allocates and uses a shared memory segment that is not part of HAL memory and thus does not burden HAL memory usage. Each data sample may contain up to twenty (20) values. The type of the values in a sample must be configured and specified in a type string. The type string consists of the following characters (case insensitive):

  • b - Boolean

  • s - Signed 32-bit

  • u - Unsigned 32-bit

  • l - Signed 64-bit

  • k - Unsigned 64-bit

  • f - Floating point (real)

Two components are available, streamer and sampler. The streamer component writes a set of pins from non-real-time data pushed into a stream. The sampler component samples a set of pins in real-time and makes them available in a stream.

Note

If you use this Python hal module interface for both read and write on the same stream, then you must create the stream with depth and type string before you can attach to it. Instantiating with depth and type string creates the stream. Instantiating without depth attaches to the stream.

Both the sampler and streamer components create the stream. You only need to attach to it from the Python hal module.

Important

A stream can only have one (1) reader and one (1) writer. The stream queue is not designed to support operation with multiple readers or writers.

Example sampler - Python sample reader:
import hal
import time

comp = hal.component("samplereader")
# Attach to the sampler stream
samplereader = hal.stream(comp, hal.sampler_base, "bffs")
# ...
comp.ready()
# ...

hal.set_value("sampler.0.enable", True) # Start streaming samples

while True:
    while samplereader.readable:
        print(samplereader.read())
    time.sleep(0.001) # Don't busy-loop
Example sampler - hal-file sampler:
loadrt sampler depth=100 cfg=bffs

# Disable sampler before adding function to the thread or it would start
# sampling immediately and could fill the queue before we start reading.
setp sampler.0.enable 0

addf sampler.0 servo-thread

net sample-jogger  motion.jog-is-active sampler.0.pin.0
net sample-joint-0 joint.0.pos-fb       sampler.0.pin.1
net sample-joint-1 joint.1.pos-fb       sampler.0.pin.2
net sample-line    motion.program-line  sampler.0.pin.3

4.1. stream constants

hal.sampler_base

The sampler component’s (sampler.c) shared memory ID for stream communication.

hal.stream_base

The streamer component’s (streamer.c) shared memory ID for stream communication.

4.2. stream methods

stream = hal.stream(comp:object, key:int, depth:int, typestr:string)

Create a stream for the component comp using the shared memory identifier key. The stream’s queue size is allocated for depth samples of typestr format. See the type string list for type meaning.

stream = hal.stream(comp:object, key:int [, typestr:string])

Attach to a stream for the component comp where the shared memory location is identified by key. If the optional typestr is provided, then it will be checked against the existing stream’s configuration. See the type string list for type meaning.

stream.read() → tuple|None

Returns a tuple of sample data from the queue. None is returned if no samples were available.

stream.write(sample:tuple)

Writes the sample argument to the stream queue. The sample must contain the correct number of elements and match the types (or be convertible) of the stream’s configuration. An IOError exception is thrown if the sample could not be written to the queue.

stream.readable() → bool

Returns True if there are samples available for reading in the stream queue or False if not.

stream.writable() → bool

Returns True if there is space available in the stream queue to hold more samples or False if not.

stream.depth() → int

Returns the number of currently available samples for read in the queue.

stream.maxdepth() → int

Returns the queue size as set when the stream was created (and cannot be changed).

stream.element_types() → int

Return a bytes object with the format type string that was used to create the stream. See type string list for individual elements and type meaning.

stream.num_underruns() → int

Returns the number of times stream.read() was called when no samples were available from the queue.

stream.num_overruns() → int

Returns the number of times stream.write() was called when no samples could be stored in the queue.

4.3. stream members

stream.sampleno

The last successfully read sample ID number as counted by the stream functions.

5. Sub-module hal.query

The hal.query sub-module is for getting information about all of HAL’s internal structures. These include:

  • Контакты

  • Параметры

  • Сигналы

  • Компонентов

  • Функции

  • Потоки

Each category can be queried by name or ID to get information about the specific item or you can get them all. The methods in this sub-module return a dictionary with all available information.

5.1. hal.query methods

hal.query.pin(name:string) → dict|None

Retrieve all information of the pin name. Returns None if the name was not found. Otherwise, returns a dictionary with the pin information:

result = hal.query.pin("my.pin.name")
result = {
    "haltype" : "pin",
    "name"    : "my.pin.name",
    "type"    : hal.Type.<BOOL,REAL,SINT,UINT,PORT,S32,U32>,
    "dir"     : hal.Dir.<IN,OUT,IO>,
    "value"   : <bool, int or float>,
    "alias"   : "alias.name"|None,
    "signal"  : "signal.name"|None,
    "comp"    : "component-name",
    "comp_id" : <int>
}
hal.query.param(name:string) → dict|None

Retrieve all information of the parameter name. Returns None if the name was not found. Otherwise, returns a dictionary with the parameter information:

result = hal.query.param("my.param.name")
result = {
    "haltype" : "parameter",
    "name"    : "my.param.name",
    "type"    : hal.Type.<BOOL,REAL,SINT,UINT,S32,U32>,
    "dir"     : hal.Dir.<RO,WR>,
    "value"   : <bool, int or float>,
    "alias"   : "alias.name"|None,
    "comp"    : "component-name",
    "comp_id" : <int>
}
hal.query.signal(name:string) → dict|None

Retrieve all information of the signal name. Returns None if the name was not found. Otherwise, returns a dictionary with the signal information:

result = hal.query.signal("my.signal.name")
result = {
    "haltype" : "signal",
    "name"    : "my.signal.name",
    "type"    : hal.Type.<BOOL,REAL,SINT,UINT,PORT,S32,U32>,
    "value"   : <bool, int or float>,
    "writers" : <int>,
    "readers" : <int>,
    "bidirs"  : <int>,
    "driver"  : "driver.pin.name"|None
}

The readers, writers and bidirs indicate how many pins are connected. There can only be one writers and many readers. Or, there can be many bidirs and many readers. The driver is the connected pin name of type hal.Dir.OUT or None if writers is zero.

hal.query.comp(name:string) → dict|None
dict|None = hal.query.comp(id:int)

Retrieve all information of the component name or by integer component id. Returns None if the name or id was not found. Otherwise, returns a dictionary with the component information:

result = hal.query.comp("component-name")
result = {
    "haltype" : "component",
    "name"    : "component-name",
    "type"    : hal.CompType.<REALTIME,USER,OTHER>
    "id"      : <int>
    "pid"     : <int>|0
    "ready"   : True|False,
    "insmod"  : "comp command line options"
}

The pid field is set to zero (0) for realtime components and the process ID for user-space components. The insmod field is the loadrt command line (excluding loadrt) and is only set for realtime components. The type field is currently only either hal.CompType.REALTIME or hal.CompType.USER.

hal.query.funct(name:string) → dict|None

Retrieve all information of the function name. Returns None if the name was not found. Otherwise, returns a dictionary with the function information:

result = hal.query.funct("my.funct.name")
result = {
    "haltype"   : "function",
    "name"      : "my.funct.name",
    "comp"      : "component-name",
    "comp_id"   : <int>,
    "users"     : <int>,
    "reentrant" : True|False
}

The users field indicates how many threads use this function. Only functions that have reentrant set to True can have more than one user.

hal.query.thread(name:string) → dict|None

Retrieve all information of the thread name. Returns None if the name was not found. Otherwise, returns a dictionary with the thread information:

result = hal.query.thread("my.thread.name")
result = {
    "haltype"   : "thread",
    "name"      : "my.thread.name",
    "comp"      : "component-name",
    "comp_id"   : <int>,
    "priority"  : <int>,
    "period"    : <int>, # in nanoseconds
    "functions" : (
        {
            "haltype" : "threadfunction",
            "name"    : "my.funct.name",
            "index"   : <int>,
            "is_init" : True|False
        },
        ...
    )
}

The functions field is a list of dictionaries. Each entry describes a function that runs in the thread. The order of execution within the thread is indicated by zero-based index. Any function in the functions list should have the corresponding users field on a hal.query.funct(name) set.
The is_init field can only contain True if the thread was setup but never run. Init functions are automatically removed once they have executed once and never show up again.

hal.query.signalpins(name:string) → dict

Retrieve information about all pins connected to signal name. Returns None if the signal name is not found. The dictionary returned may be empty and has the format:

result = hal.query.signalpins("my.signal")
result = {
    "pin-r" : { "haltype": "pin", "name": "pin-r", "dir": hal.Dir.IN  "signal": "my.signal", ...},
    "pin-w" : { "haltype": "pin", "name": "pin-w", "dir": hal.Dir.OUT "signal": "my.signal", ...},
    "pin-x" : { "haltype": "pin", ...},
    ...
}

See hal.query.pin() for description for pin dictionary details.

hal.query.pins() → dict

Retrieve information about all pins. The dictionary returned has the format:

{
    "pin-a" : { "haltype": "pin", "name": "pin-a", "type":...},
    "pin-b" : { "haltype": "pin", ...},
    ...
}

See hal.query.pin() for description of the pin dictionary details.

hal.query.params() → dict

Retrieve information about all params. The dictionary returned has the format:

{
    "param-a" : { "haltype": "parameter", "name": "param-a", "type":...},
    "param-b" : { "haltype": "parameter", ...},
    ...
}

See hal.query.param() for description of the param dictionary details.

hal.query.signals() → dict

Retrieve information about all signals. The dictionary returned has the format:

{
    "sig-a" : { "haltype": "signal", "name": "sig-a", "type":...},
    "sig-b" : { "haltype": "signal", ...},
    ...
}

See hal.query.signal() for description of the signal dictionary details.

hal.query.comps() → dict

Retrieve information about all components. The dictionary returned has the format:

{
    "comp-a" : { "haltype": "component", "name": "comp-a", "type":...},
    "comp-b" : { "haltype": "component", ...},
    ...
}

See hal.query.comp() for description of the component dictionary details.

hal.query.functs() → dict

Retrieve information about all functions. The dictionary returned has the format:

{
    "funct-a" : { "haltype": "function", "name": "funct-a", ...},
    "funct-b" : { "haltype": "function", ...},
    ...
}

See hal.query.funct() for description of function dictionary details.

hal.query.threads() → dict

Retrieve information about all threads. The dictionary returned has the format:

{
    "thread-a" : { "haltype": "thread", "name": "thread-a", ...},
    "thread-b" : { "haltype": "thread", ...},
    ...
}

See hal.query.thread() for description of thread dictionary details.

6. Class hal.shm

The shm class is an interface to create and manage shared memory segments.

The shm class should be considered experimental and may or may not work as intended.

Warning

Do not rely on this class. It may be removed in future releases.

6.1. shm methods

shm = hal.shm(comp:object, key:int, size:int)

Allocate a size sized shared memory segment with ID key.

shm.getbuffer()

Returns a Python memoryview object of the shared memory segment.

shm.setsize()

This is non-functional. Do not use. You cannot increase or decrease the shared memory segment’s size once it is created.

7. HAL Port pipes

A HAL port is a byte-oriented pipe that streams bytes from the writer to the reader. It may be used to transport data between real-time and non-real-time in either direction. The HAL port pipe facility is distinct from the HAL stream facility in that it has no concept of typed data. The reader and writer of a port must handle a binary byte oriented data-stream. A HAL port uses HAL pins of type hal.Type.PORT to communicate.

Example - HAL port data writer:
import hal
import time
import struct

comp = hal.component("portwriter")

# Create the write-end
portw = comp.newpin("portpin", hal.Type.PORT, hal.Dir.OUT)

# Create a signal to link reader and writer
portsig = hal.new_sig("portsig", hal.Type.PORT)
portw.ready()

# Connect the pins to the signal net
hal.connect("portwriter.portpin", "portsig")
hal.connect("portreader.portpin", "portsig")

# Allocate and set the port's queue size
hal.set_s("portsig", 256)

while True:
    if portw.writable() < 2:
        time.sleep(0.001) # Don't busy-loop
        continue
    cmd, arg = read_command()
    binvals = struct.pack("bb", cmd, arg)
    portw.write(binvals)
Example - HAL port data reader component:
component portreader "Reads data from a HAL port";
pin in port portpin  "Port's read end";

description "Port reader example component";

option singleton;
option period no;
license "GPL";
function _;

;;
void do_abort(void) { /* you write code here */ }
void kill_switch(char x) { (void)x; /* you write some more code here */ }

FUNCTION(_)
{
    unsigned avail = hal_port_readable(portpin_ptr);
    if(avail > 1) {
        rtapi_u8 buf[2];
        if(!hal_port_read(portpin_ptr, buf, sizeof(buf))) {
            rtapi_print_msg(RTAPI_MSG_ERR, "Port read failed\n");
            hal_port_clear(portpin_ptr);  // Try to recover
        } else {
            switch(buf[0]) {
            case 'a': do_abort(); break;
            case 'k': kill_switch(buf[1]); break;
            // ...
            }
        }
    }
}
Example - HAL port hal file to load the reader:
loadrt portreader

addf portreader servo.thread

A more comprehensive implementation can be found in the raster.comp component together with the raster programmer.

Important

A HAL port pipes can only have one (1) reader and one (1) writer. The port queue is not designed to support operation with multiple readers or writers.

Note

A HAL port queue cannot be allocated larger than 64 kiB (65536 bytes). The minimum size is one (1) byte, but that is not recommended. You should analyze your usage and find the appropriate size to set.

7.1. Port pin methods

port = comp.newpin(name:string, hal.Type.PORT, io:enum)

A port is a special pin type. It is created as a normal pin with type hal.Type.PORT. You need two pins for a port, one input and one output. Both ends of a port are connected with a signal (net). Setting the signal will allocate the port’s queue.

port.readable() → int

Returns the number of bytes available for reading from the port queue.

port.writable() → int

Returns the number of bytes possible to write to the port queue.

port.write(data:bytes) → bool

Write a buffer onto the port queue. The argument data can be either a bytes buffer or a string. If it is a string, then it will be converted to a UTF-8 bytes buffer. Returns True if the data was successfully written to the port queue and False if not. The write() call fails if the port queue has not enough free capacity for the entire data argument.

Note

You should be careful when writing a string. Strings are Unicode encoded and may expand to multiple bytes when converted to UTF-8. Therefore, the length of the string may not match the length of the UTF-8 bytes buffer and not fit into the queue.

port.read(size:int) → bytes

Reads size bytes from the port and removes the bytes from the port queue. The port is tested whether size bytes are available before attempting to read. Returns a bytes buffer upon success or False on failure.

port.peek(size:int) → bytes

Reads size bytes from the port without removing the bytes from the port queue. The port is tested whether size bytes are available before attempting to peek. Returns a bytes buffer upon success or False on failure.

port.peek_commit(size:int) → bool

Removes size bytes from the port queue. Returns True on success or False if not.

Example:
def wait_for(n):
    while port.readable() < n:
        time.sleep(0.01)

wait_for(2)
# Peek at the first 2 bytes of the data pipe
data = port.peek(2)
# Check the data for a pattern
if data[0] == 123 and data[1] == 42:
    port.peek_commit(2)  # Discard data from the pipe
else:
    # Not the discardable pattern, need 4 bytes then
    wait_for(4)
    data = port.read(4)
    handle_data(data)
port.clear()

Clears the content of the port queue.

port.size() → int

Return the allocated port queue size. The return value is zero (0) if no queue was allocated for the port.