> For the complete documentation index, see [llms.txt](https://docs.uniot.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.uniot.io/platform/sandbox/visual-editor/primitives.md).

# Primitives

Primitive blocks provide direct access to device hardware. Use these blocks to read sensors, control digital and analog outputs, handle button inputs, and create custom hardware interactions.

## Understanding Registers

Primitives use [register](/general-concepts/primitives.md#registers) indices instead of physical pin numbers. The register system organizes pins into categories (digital input/output, analog input/output) and assigns each an index. View your device's pin-to-register mapping in the Registers tab on the Uniot Platform.

## Return Values in Primitives

Primitives can either return a value or perform an action without returning meaningful data. The visual blocks for default primitives (like `analog read`, `digital read`, etc.) are predefined and correctly reflect whether they return values or not.

For user-defined primitives, the visual editor cannot automatically determine whether a primitive should return a value or just perform an action. Since primitives are C++ functions called by the UniotLisp Interpreter, every function must return something—even action-only primitives return a boolean value. Therefore, user primitive blocks are rendered with a return value by default. If your primitive doesn't need to return a meaningful value, you can disable the return value field in the block configuration.

## analog read

<div align="left"><figure><img src="/files/oK6j4Hs28nPfBfi0l1Kb" alt=""><figcaption></figcaption></figure></div>

Reads an analog value from the specified register. Use this for sensors that output variable voltage levels.

**Parameters:**

* **Register** (Number): The analog input register index

**Returns:**

* **Number**: Value between 0-1023 (10-bit ADC resolution)

**Example:**

<figure><img src="/files/tgDFnxIkjKw0Hu4F9Nvh" alt=""><figcaption><p>Check light level and push event if it's too low</p></figcaption></figure>

## analog write

<div align="left"><figure><img src="/files/HNH4yNBP2hVYoxpZRBAG" alt=""><figcaption></figcaption></figure></div>

Writes an analog (PWM) value to the specified register. Use this to control LED brightness, motor speed, or other variable outputs.

**Parameters:**

* **Register** (Number): The analog output register index
* **Value** (Number): Value between 0-1023 (PWM duty cycle)

**Example:**

<figure><img src="/files/GEYLIlPcU3YQBQLUrrCw" alt=""><figcaption><p>Receive brightness value from dashboard and set LED accordingly</p></figcaption></figure>

## digital read

<div align="left"><figure><img src="/files/a3fqTmIV9YuVUm1myFHC" alt=""><figcaption></figcaption></figure></div>

Reads the digital state from the specified register (HIGH or LOW).

**Parameters:**

* **Register** (Number): The digital input register index

**Returns:**

* **Boolean**: `#t` for HIGH, `()` for LOW

**Example:**

<figure><img src="/files/QSB5kyZVrA2VAWBMf1kc" alt=""><figcaption><p>Read switch state and push event when changed</p></figcaption></figure>

## digital write

<div align="left"><figure><img src="/files/kDYr0PrnKnJ8blKd1CZe" alt=""><figcaption></figcaption></figure></div>

Sets a digital register to HIGH or LOW. Use this to control LEDs, relays, or other on/off outputs.

**Parameters:**

* **Register** (Number): The digital output register index
* **Value** (Boolean): `#t` for HIGH, `()` for LOW

**Example:**

<figure><img src="/files/U9PWVuo57LXOQ5uby8fC" alt=""><figcaption><p>Check for event and control LED state</p></figcaption></figure>

## button clicked

<div align="left"><figure><img src="/files/3DTLjbyD22wLVJXl1DON" alt=""><figcaption></figcaption></figure></div>

Detects button press events on the specified register. Returns true when a press-and-release cycle is detected, then resets until the next click.

**Parameters:**

* **Register** (Number): The digital input register index

**Returns:**

* **Boolean**: `#t` if clicked since last check, `()` otherwise

**Example:**

<figure><img src="/files/x1405XIewileOBVv0kLf" alt=""><figcaption><p>Toggle LED state on button click</p></figcaption></figure>

## user primitive (template)

<div align="left"><figure><img src="/files/Y5Eg7cUvKFhhOj6mMrpY" alt=""><figcaption></figcaption></figure></div>

Creates a custom primitive definition that you'll implement in your firmware. Click the gear icon to configure parameters and return type.

**Configuration:**

* **Primitive Name**: Unique identifier for your custom primitive
* **Parameters**: Define input values the primitive accepts
* **Return Type**: Configure what type of value it returns (boolean by default, or none for action-only primitives)

{% hint style="info" %}
**Note on return values**: Since primitives are C++ functions called by the UniotLisp Interpreter, every function must return a value. If your primitive performs an action without needing to return a meaningful value, it should still return a boolean. The visual block is rendered with a return value by default. If you don't need to use the return value, you have to disable the corresponding field in the block configuration.
{% endhint %}

**Use cases**: Creating custom sensor drivers, protocol handlers, complex hardware control, or specialized operations not covered by standard primitives.

## user primitive (autogenerated)

<div align="left"><figure><img src="/files/FRBn67XTWq47Av9dToMl" alt=""><figcaption></figcaption></figure></div>

Calls a custom primitive already defined in your device firmware. These blocks are automatically generated based on primitives registered in your firmware code.

**Parameters:**

* **Arguments**: Values matching the primitive's defined parameters

**Returns:**

* **Type varies**: Depends on how the primitive was defined in firmware

{% hint style="info" %}
**Note on return values**: Since the visual editor cannot determine whether a user primitive is intended to return a value or just perform an action, autogenerated blocks are rendered with a return value by default. This is because all C++ functions must return something—even action-only primitives return a boolean value. If your primitive doesn't need to return a meaningful value, you have to disable the return value field in the block configuration.
{% endhint %}

**Example:**

<figure><img src="/files/ddoO4tOqjfV0eN1O0Gs1" alt=""><figcaption><p>Custom LED strip control</p></figcaption></figure>
