Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Building a Web HMI

HMI Overview

Every AutoCore project includes a www/ directory that contains a React + TypeScript web application. This web app connects to the server via WebSocket and can:

  • Display live variable values
  • Send commands (write variables, trigger actions)
  • Show logs and status information

The web HMI is served directly by the AutoCore server. After deploying, you open it in any web browser.

The AutoCore React Library

The @adcops/autocore-react library provides React hooks for connecting to the AutoCore server and working with variables. The project template includes this library pre-configured.

Key hooks:

HookPurpose
useAutoCoreConnection()Connect to the server WebSocket
useVariable(name)Subscribe to a variable and get its live value
useCommand()Send commands to the server

Creating a Simple Dashboard

Here is a basic HMI that shows the motor speed and provides start/stop controls. Edit www/src/App.tsx:

import React from 'react';
import { useVariable, useCommand } from './AutoCore';
import { Button } from 'primereact/button';
import { Knob } from 'primereact/knob';

function App() {
    // Subscribe to live variable values
    const motorRunning = useVariable<boolean>('machine_running');
    const speedActual = useVariable<number>('motor_speed_actual');
    const speedSetpoint = useVariable<number>('motor_speed_setpoint');

    // Command hook for writing variables
    const sendCommand = useCommand();

    const handleStart = () => {
        sendCommand('gm.write', { name: 'machine_running', value: true });
    };

    const handleStop = () => {
        sendCommand('gm.write', { name: 'machine_running', value: false });
    };

    const handleSpeedChange = (rpm: number) => {
        sendCommand('gm.write', { name: 'motor_speed_setpoint', value: rpm });
    };

    return (
        <div style={{ padding: '2rem' }}>
            <h1>Motor Control</h1>

            <div style={{ display: 'flex', gap: '2rem', alignItems: 'center' }}>
                <div>
                    <h3>Speed</h3>
                    <Knob
                        value={speedActual?.value ?? 0}
                        max={2000}
                        readOnly
                        valueTemplate="{value} RPM"
                        size={150}
                    />
                </div>

                <div>
                    <h3>Setpoint</h3>
                    <Knob
                        value={speedSetpoint?.value ?? 0}
                        max={2000}
                        onChange={(e) => handleSpeedChange(e.value)}
                        valueTemplate="{value} RPM"
                        size={150}
                    />
                </div>

                <div>
                    <h3>Controls</h3>
                    <Button
                        label="Start"
                        icon="pi pi-play"
                        onClick={handleStart}
                        disabled={motorRunning?.value === true}
                        severity="success"
                        style={{ marginRight: '1rem' }}
                    />
                    <Button
                        label="Stop"
                        icon="pi pi-stop"
                        onClick={handleStop}
                        disabled={motorRunning?.value !== true}
                        severity="danger"
                    />
                </div>
            </div>

            <p>
                Status: {motorRunning?.value ? 'RUNNING' : 'STOPPED'}
            </p>
        </div>
    );
}

export default App;

Subscribing to Live Variable Updates

When you use useVariable(name), the library automatically subscribes to that variable via WebSocket. The value updates in real time whenever the control program changes it — there is no polling.

Under the hood, this sends a subscribe message to the server:

{ "domain": "gm", "fname": "subscribe", "args": { "name": "motor_speed_actual" } }

The server then pushes updates whenever the value changes:

{ "domain": "gm", "fname": "broadcast", "args": { "name": "motor_speed_actual", "value": 1247.5 } }

Sending Commands from the HMI

To write a variable value:

sendCommand('gm.write', { name: 'motor_speed_setpoint', value: 1500 });

To read a variable on demand (instead of subscribing):

const result = await sendCommand('gm.read', { name: 'motor_speed_actual' });
console.log(result.value);

To send a command to an external module:

sendCommand('modbus.status', {});
sendCommand('ethercat.get_state', { slave: 'ClearPath_0' });

Deploying the HMI

Build and deploy the web HMI:

cd www
npm install       # First time only
npm run build     # Creates www/dist/
cd ..
acctl push www    # Uploads dist/ to the server

Then open your browser to http://<server_ip>:8080 to see the HMI.

During development, you can run the HMI in development mode with hot reloading:

cd www
npm run dev

This starts a local dev server (usually at http://localhost:5173). You will need to configure the WebSocket URL to point to your AutoCore server — check www/src/AutoCore.ts for the connection settings.