Skip to content

Widgets Reference

RadioKit v2.0 uses a Fields struct + rk member pattern. All widget state is accessed through the public rk member:

// Read widget state
bool state = button.rk.state;
int8_t value = slider.rk.value;
// Write widget state (auto-synced on next RadioKit.update())
led.rk.state = true;
led.rk.color = 0xFF0000;

There are no getter/setter methods. All access goes through rk fields directly.

Constructors take positional parameters: (x, y, height, width = 0, rotation = 0).

// Minimal constructor — only spatial params
RK_Slider slider(100, 60, 12, 80);
// Post-construction: set optional fields via rk
slider.rk.label = "Speed";
slider.rk.centering = RK_SPRING_CENTER;

When you write to any rk field, the change is automatically detected on the next RadioKit.update() and pushed to the app.


The primary binary input widgets.

Fields (RK_ButtonFields):

struct RK_ButtonFields {
uint8_t x, y;
uint8_t height, width;
int16_t rotation;
const char* icon, *onText, *offText, *label;
bool active; ///< Transport active flag
bool state; ///< Push/toggle state
};

Access Pattern:

AccessCode
Read statebool pressed = btn.rk.state;
Write statebtn.rk.state = true;
Set onTextbtn.rk.onText = "ON";
Set offTextbtn.rk.offText = "OFF";
Set iconbtn.rk.icon = "power";

Examples:

// Push button
RK_PushButton fire(20, 50, 20);
fire.rk.label = "FIRE";
fire.rk.icon = "flame";
fire.rk.onText = "ON";
fire.rk.offText = "OFF";
// Toggle button
RK_ToggleButton power(20, 80, 20);
power.rk.label = "Power";
power.rk.icon = "power";
// Usage in loop()
void loop() {
RadioKit.update();
if (fire.rk.state) { triggerFire(); }
if (power.rk.state) { enableSystems(); }
}

Edge detection (rising/falling edge):

static bool lastState = false;
if (btn.rk.state && !lastState) {
// Rising edge — button was just pressed
}
if (!btn.rk.state && lastState) {
// Falling edge — button was just released
}
lastState = btn.rk.state;

Selection groups (Radio or Checkbox).

Fields (RK_MultipleFields):

struct RK_MultipleFields {
uint8_t x, y;
uint8_t height, width;
int16_t rotation;
const char* label;
bool active;
uint8_t value; ///< Bitmask (bit 0 = item 0, ...)
uint8_t variant; ///< 0=Segments, 1=Grid, 2=Wheel
RK_Item items[8]; ///< Item pool
uint8_t itemCount;
};
struct RK_Item {
const char* label; ///< Display text
const char* icon; ///< Optional icon name
uint8_t pos; ///< Bitmask position (0-7)
};

Access Pattern:

AccessCode
Read bitmaskuint8_t mask = multi.rk.value;
Check itembool active = (multi.rk.value & (1 << i)) != 0;
Set itemsmulti.rk.items[0] = {"WiFi", "wifi", 0};
Set item countmulti.rk.itemCount = 3;

Example:

RK_MultipleSelect toolbar(50, 85, 10);
toolbar.rk.label = "Gear";
toolbar.rk.items[0] = {"D", "drive_eta", 0};
toolbar.rk.items[1] = {"P", "local_parking", 1};
toolbar.rk.items[2] = {"R", "settings_backup_restore", 2};
toolbar.rk.itemCount = 3;
void loop() {
RadioKit.update();
if ((toolbar.rk.value & (1 << 0)) != 0) { /* D selected */ }
if ((toolbar.rk.value & (1 << 1)) != 0) { /* P selected */ }
}

Slide/rocker switch for binary on/off control.

Fields (RK_SlideSwitchFields):

struct RK_SlideSwitchFields {
uint8_t x, y;
uint8_t height, width;
int16_t rotation;
const char* icon, *onText, *offText, *label;
uint8_t variant; ///< 0=Slide Switch, 1=Rocker Switch
bool labelHidden;
bool active;
bool state;
};

Access Pattern:

AccessCode
Read statebool s = sw.rk.state;
Set statesw.rk.state = true;
Set textsw.rk.onText = "ON"; sw.rk.offText = "OFF";
Hide labelsw.rk.labelHidden = true;

Example:

RK_SlideSwitch headlights(50, 60, 15);
headlights.rk.label = "Headlights";
headlights.rk.icon = "sun";
headlights.rk.onText = "ON";
headlights.rk.offText = "OFF";
void loop() {
RadioKit.update();
digitalWrite(LED_PIN, headlights.rk.state ? HIGH : LOW);
}

Linear analog input control (-100 to +100).

Fields (RK_SliderFields):

struct RK_SliderFields {
uint8_t x, y;
uint8_t height, width;
int16_t rotation;
const char* label;
bool active;
int8_t value; ///< -100 to +100
uint8_t centering; ///< Spring mode
uint8_t detents; ///< 0 = continuous, 1-31 = snap positions
};

Access Pattern:

AccessCode
Read valueint8_t v = slider.rk.value;
Write valueslider.rk.value = 50;
Set centeringslider.rk.centering = RK_SPRING_NONE;
Set detentsslider.rk.detents = 5;

Example:

// Continuous horizontal slider
RK_Slider throttle(50, 40, 10, 80);
throttle.rk.label = "Throttle";
throttle.rk.centering = RK_SPRING_NONE;
// Gas pedal (springs to minimum)
RK_GasPedal gas(80, 40, 10);
gas.rk.label = "Gas";
void loop() {
RadioKit.update();
int8_t gasVal = gas.rk.value;
setMotorSpeed(map(gasVal, -100, 100, 0, 255));
}

Rotary analog input control (-100 to +100).

Fields (RK_KnobFields):

struct RK_KnobFields {
uint8_t x, y;
uint8_t height, width;
int16_t rotation;
const char* icon, *label;
bool active;
int8_t value; ///< -100 to +100
int16_t startAngle; ///< Default -135
int16_t endAngle; ///< Default 135
uint8_t centering; ///< Spring mode
uint8_t detents; ///< 0 = continuous, 1-63 = snap
uint8_t variant; ///< 0=standard, 1=steeringWheel
const char* centerIcon; ///< Icon in the middle of knob
};

Access Pattern:

AccessCode
Read valueint8_t v = knob.rk.value;
Set valueknob.rk.value = 25;
Set anglesknob.rk.startAngle = -90; knob.rk.endAngle = 90;

Example:

// Standard knob
RK_Knob vol(140, 40, 10);
vol.rk.label = "Vol";
vol.rk.icon = "volume-2";
// Steering wheel (auto-centering knob variant)
RK_Knob steering(85, 60, 30);
steering.rk.label = "Steering";
steering.rk.startAngle = -150;
steering.rk.endAngle = 150;
steering.rk.centering = RK_SPRING_CENTER;
steering.rk.variant = 1; // steeringWheel
void loop() {
RadioKit.update();
int8_t steer = steering.rk.value;
setSteeringPosition(steer);
}

2-axis analog controller (-100 to +100).

Fields (RK_JoystickFields):

struct RK_JoystickFields {
uint8_t x, y;
uint8_t height, width;
int16_t rotation;
const char* icon, *label;
bool enabled;
bool active;
int8_t xvalue, yvalue; ///< -100 to +100 each
uint8_t centering;
};

Access Pattern:

AccessCode
Read Xint8_t x = joy.rk.xvalue;
Read Yint8_t y = joy.rk.yvalue;

Example:

RK_Joystick drive(180, 50, 20);
drive.rk.label = "Drive";
drive.rk.centering = RK_SPRING_CENTER;
void loop() {
RadioKit.update();
int8_t x = drive.rk.xvalue; // Left/Right
int8_t y = drive.rk.yvalue; // Forward/Back
setMotorSpeeds(y + x, y - x);
}

Visual status indicator (Arduino -> App).

Fields (RK_LedFields):

struct RK_LedFields {
uint8_t x, y;
uint8_t height, width;
int16_t rotation;
const char* label;
uint32_t color; ///< RGB hex (e.g. 0xFF0000)
bool state; ///< ON/OFF
uint8_t shape; ///< 0=circle, 1=square, 2=diamond, 3=star
uint8_t ledState; ///< 0=OFF, 1=ON, 2=BLINK, 3=BREATHE
uint16_t timing; ///< ms for blink/breathe
};

Access Pattern:

ActionCode
Turn onled.rk.state = true;
Turn offled.rk.state = false;
Set colorled.rk.color = 0x00FF00;
Set shapeled.rk.shape = RK_LED_SHAPE_SQUARE;
Set blinkled.rk.ledState = RK_LED_STATE_BLINK;
Set timingled.rk.timing = 300;

Example:

RK_LED status(20, 20, 14);
status.rk.label = "Status";
void loop() {
RadioKit.update();
if (linkEstablished) {
status.rk.state = true;
status.rk.color = 0x00FF00; // Green
} else {
status.rk.state = false;
}
}

Dynamic text display (Arduino -> App).

Fields (RK_TextFields):

struct RK_TextFields {
uint8_t x, y;
uint8_t height, width;
const char* label;
const char* content; ///< Pointer to text content
};

Access Pattern:

ActionCode
Set texttext.rk.content = "Hello";
Read textconst char* t = text.rk.content;

Note: rk.content is a pointer. The pointed-to string must outlive the current loop iteration. Use static buffers or string literals.

Example:

RK_Text status(50, 10, 10);
status.rk.label = "LOG:";
status.rk.content = "System Ready";
// Serial Monitor in the app
RK_Serial serialMonitor(100, 80, 20, 180);
serialMonitor.rk.label = "Serial Console";
void loop() {
RadioKit.update();
static char buf[32];
snprintf(buf, sizeof(buf), "Uptime: %lus", millis() / 1000);
status.rk.content = buf;
serialMonitor.println("Log entry");
}

Display-only widget for telemetry values (Arduino -> App). Rendered in the active link card, not on the control canvas.

Fields (RK_TelemetryFields):

struct RK_TelemetryFields {
const char* label;
const char* icon;
const char* unit;
const char* content; ///< Dynamic text value
};

Access Pattern:

ActionCode
Set valuetelemetry.rk.content = "25.5";
Set icontelemetry.rk.icon = "thermometer";
Set unittelemetry.rk.unit = "C";

Example:

RK_Telemetry temp("Temperature");
temp.rk.icon = "thermometer";
temp.rk.unit = "C";
temp.rk.content = "25.5";

ConstantValueShape
RK_LED_SHAPE_CIRCLE0Circle
RK_LED_SHAPE_SQUARE1Square
RK_LED_SHAPE_DIAMOND2Diamond
RK_LED_SHAPE_STAR3Star
ConstantValueBehaviour
RK_LED_STATE_OFF0Off (no light)
RK_LED_STATE_ON1Solid on
RK_LED_STATE_BLINK2Blinking
RK_LED_STATE_BREATHE3Breathing (fade in/out)
ConstantValueBehaviour
RK_SEGMENTS0Segmented button group
RK_GRID1Grid layout
RK_WHEEL2Wheel/selector
ConstantValueUsage
RK_PRIMARY0Primary style
RK_DIM1Dim/secondary
RK_SUCCESS2Green/success
RK_WARNING3Yellow/warning
RK_DANGER4Red/danger

  1. Always call update() — Never block in loop() with delay() or long computations.
  2. Declare widgets globally — They self-register on construction.
  3. Configure before begin()config fields are read-only after begin().
  4. Use consistent buffer lifetimes — Text content via rk.content must point to persistent storage (static variables or string literals). Stack-local buffers will dangle.
  5. Keep loop() fast — Defer heavy work to timers or state machines.
  6. Max 16 widgets — Plan your UI to fit within RADIOKIT_MAX_WIDGETS.