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 bits8N1— 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));
});