Scripting guide

EMWaver scripts are local JavaScript programs. Scripts use .js files and can include JSX-style UI syntax for native panels, extensionless imports such as "emw-ui", and hardware modules such as emw-gpio, emw-spi, and emw-sampler.

How scripts run

  1. The app loads the visible runtime libraries from the bundled script assets.
  2. Imports are resolved from EMWaver modules such as emw-ui, emw-jsx, emw-gpio, and emw-spi.
  3. JSX-style syntax is transpiled into native EMWaver UI nodes.
  4. Your script renders a native panel with render(<App />).
  5. Callbacks such as onTap, onChange, and onViewportChange handle user events and then re-render.

Hardware calls are synchronous from the script authoring point of view: a GPIO, SPI, I2C, UART, ADC, PWM, or sampler call returns after the local app/board path responds or throws.

Minimal JSX script

import { JSX, render } from "emw-jsx";
import { Column, Text, Button } from "emw-ui";

let count = 0;

function increment() {
  count += 1;
  rerender();
}

function reset() {
  count = 0;
  rerender();
}

function App() {
  return (
    <Column padding={16} spacing={12}>
      <Text font="title2" fontWeight="semibold">Hello</Text>
      <Text>Count: {count}</Text>
      <Button onTap={increment}>Increment</Button>
      <Button onTap={reset}>Reset</Button>
    </Column>
  );
}

function rerender() {
  render(<App />);
}

rerender();

Script structure

  • Imports: bring in the UI renderer, JSX factory, components, and hardware modules.
  • State: keep state in normal JavaScript variables.
  • Actions: callbacks mutate state, perform device work, then re-render.
  • App component: returns a JSX-style tree made from EMWaver UI components.
  • Render function: one small render(<App />) wrapper updates the native panel.

Board-aware GPIO example

import { JSX, render } from "emw-jsx";
import { Column, Text, Button, Picker } from "emw-ui";
import { pin, gpio } from "emw-gpio";

function boardType() {
  try { return device.boardType(); } catch (e) { return "stm32f042"; }
}

const board = boardType();
const options = board === "esp32s3"
  ? [{ label: "GPIO4", value: "4" }, { label: "GPIO37", value: "37" }]
  : [{ label: "A0", value: "0" }, { label: "A1", value: "1" }];

let selected = options[0].value;
let high = false;

function target() {
  const n = Number(selected);
  return board === "esp32s3" ? pin({ gpio: n }) : pin({ port: "A", number: n });
}

function toggle() {
  high = !high;
  gpio.mode(target(), "output");
  gpio.write(target(), high);
  rerender();
}

function App() {
  return (
    <Column padding={16} spacing={12}>
      <Text font="title2" fontWeight="semibold">GPIO</Text>
      <Text font="caption">Detected: {board}</Text>
      <Picker
        style="menu"
        selected={selected}
        options={options}
        onChange={(value) => { selected = String(value); rerender(); }}
      />
      <Button onTap={toggle}>{high ? "Write LOW" : "Write HIGH"}</Button>
    </Column>
  );
}

function rerender() { render(<App />); }
rerender();

Bundled modules

ModuleExportsUse
emw-jsxJSX, h, renderJSX transform target and UI rendering.
emw-uiUI componentsNative UI tree components such as Column, Button, and Plot.
emw-gpiopin, gpioPin encoding, GPIO mode/read/write.
emw-spispiSPI transfers with optional chip select.
emw-i2ci2cI2C open/read/write/write-then-read flows.
emw-uartuartUART open/read/write flows.
emw-adcadcADC pin/internal-source reads and resolution setting.
emw-pwmpwmPWM writes and resolution setting.
emw-fsFSApp-local file helpers.
emw-samplerSampler, SamplerSignalsSignal capture, buffers, replay, and saved signal access.

Built-in scripts

The app bundles local examples as JavaScript source. They are both tools and reference implementations.

ScriptWhat it demonstrates
hello.jsJSX/import smoke test with buttons and state.
blink.jsBoard-aware pin selection, timer-based output, GPIO module use.
sampler.jsSignal capture, plot viewport callbacks, retransmit, local save/load.
cc1101.jsCC1101 radio register control, presets, SPI transfers, logs, grids, cards, tiles.
rfid.jsMFRC522 probe/UID/read/write workflows over SPI and GPIO reset/IRQ pins.
rfm69.jsProfile-driven RFM69/RFM69HW radio control and RSSI/status UI.

Timing

delay(500);          // blocking sleep (ms)
sleep(100);          // alias for delay
millis();            // milliseconds since the script engine started

const timer = every(1000, () => {
  // periodic work; return false to stop, or call timer.stop()
  rerender();
});
timer.stop();

Use rendered panels for user interaction. Agent hardware automation should use named hardware primitives such as SPI transfers, GPIO reads/writes, analog reads, and module probes rather than screen-scraping UI state.

Next

  • UI widgets for every JSX component and prop shape.
  • Device API for GPIO, buses, sampler, files, and device info.