Skip to main content
Version: 1.0

UART

UART communication uses a serial byte stream to exchange data with an external microcontroller. The device writes strings out on the port and receives incoming data through a callback.

Getting the UART Handle

const uart = UART.get(0);

Parameters:

  • port — UART port number (0, 1, etc.)

Configuring the Port

Apply baud rate and serial mode before reading or writing:

uart.configure({ baud: 115200, mode: '8N1' });

Parameters:

  • baud — Baud rate (9600, 19200, 38400, 57600, 115200, etc.)
  • mode — Data format: 3-character string denoting data bits, parity and stop bits
    • 8N1 — 8 data bits, no parity, 1 stop bit
    • Other modes: 8E1, 8O1, etc.

Writing Data

Send strings to the external device:

uart.write('HELLO\n');
uart.write('STATE:ON\n');
uart.write('DIM:50\n');

Reading Data

Register a callback to receive incoming data:

uart.recv((data: string) => {
console.log('Received:', data);
// Parse and handle the data
});

Bidirectional Synchronization Pattern

The core pattern for synchronizing hardware with virtual components. Unlike TMCU, UART is a raw stream, so the script owns both the wire format and the loop-prevention logic.

Device to App

Parse incoming UART data and update the virtual component. Use a flag to remember that the update originated from hardware, so the corresponding change handler does not echo the value back out:

const stateHandle = Service.getVCHandle('state');
const uart = UART.get(0);
uart.configure({ baud: 115200, mode: '8N1' });

let updatingFromUart = false;

uart.recv((data: string) => {
updatingFromUart = true;
stateHandle.setValue(data === 'CMD_ON');
updatingFromUart = false;
});

App to Device

Listen for virtual component changes (user interactions) and write to hardware. Skip the write when the update came from UART itself:

stateHandle.on('change', (data: any) => {
if (updatingFromUart) return;
uart.write(data.value ? 'CMD_ON' : 'CMD_OFF');
});

UART Examples

Simple Boolean Switch

For on/off controls:

const uart = UART.get(0);
uart.configure({ baud: 115200, mode: '8N1' });

const stateHandle = Service.getVCHandle('state');

let updatingFromUart = false;

// Device to app
uart.recv((data: string) => {
updatingFromUart = true;
stateHandle.setValue(data === 'CMD_ON');
updatingFromUart = false;
});

// App to device
stateHandle.on('change', (data: any) => {
if (updatingFromUart) return;
uart.write(data.value ? 'CMD_ON' : 'CMD_OFF');
});

State and Brightness

For a switch paired with a level control, encoded as a single numeric value on the wire:

const uart = UART.get(0);
uart.configure({ baud: 115200, mode: '8N1' });

const stateHandle = Service.getVCHandle('state');
const brightnessHandle = Service.getVCHandle('brightness');

let updatingFromUart = false;

// Device to app
uart.recv((data: string) => {
updatingFromUart = true;
const value = parseInt(data, 10);
stateHandle.setValue(value > 0);
brightnessHandle.setValue(value);
updatingFromUart = false;
});

// App to device
stateHandle.on('change', (data: any) => {
if (updatingFromUart) return;
uart.write('LEVEL:' + (data.value ? '100' : '0'));
});

brightnessHandle.on('change', (data: any) => {
if (updatingFromUart) return;
uart.write('LEVEL:' + String(data.value));
});

Button Actions

For button components (which have no stored value) combined with a position sensor:

const uart = UART.get(0);
uart.configure({ baud: 115200, mode: '8N1' });

const openHandle = Service.getVCHandle('open');
const closeHandle = Service.getVCHandle('close');
const positionHandle = Service.getVCHandle('position');

let updatingFromUart = false;

// Device to app
uart.recv((data: string) => {
updatingFromUart = true;
const value = parseInt(data, 10);
positionHandle.setValue(value);
updatingFromUart = false;
});

// App to device
openHandle.on('single_push', () => {
uart.write('ACTION_EXPAND');
});

closeHandle.on('single_push', () => {
uart.write('ACTION_COLLAPSE');
});

positionHandle.on('change', (data: any) => {
if (updatingFromUart) return;
uart.write('POSITION:' + String(data.value));
});