Skip to content

Robulus Code Writing Guide

Audience: Robot builders, LLMs writing firmware code, and anyone creating user logic for a Robulus unit in Juvantia Fabrica.


1. How Robulus Firmware Works

The ESP32-S3 has two CPU cores. Juvantia splits them:

CoreOwnerRunsYou Can Modify?
Core 0SystemWi-Fi, WebSocket, heartbeat, command parsing, OTA❌ No
Core 1UserYour code — user_setup(), user_loop(), deck()✅ Yes

Execution Flow

POWER ON

  ├─── Core 0: System boots
  │      • Connects to Wi-Fi (hardcoded park / user-configured home network)
  │      • Opens WebSocket to deck.juvantia.org
  │      • Starts heartbeat (every 5s)
  │      • Waits for commands from Deck Control

  └─── Core 1: User code boots
         • Calls user_setup() ONCE
         • Enters infinite loop:
              while(true) {
                  user_loop();   // your continuous logic
                  yield();       // allow system tasks
              }

WHEN DECK CONTROL SENDS A COMMAND:
  Core 0 receives it via WebSocket
  Core 0 parses it: { type: "AXIS", name: "Drive", value: 75 }
  Core 0 calls → deck("AXIS", "Drive", 75) on Core 1
  Your code handles it

2. The Three Functions You Must Implement

Your code file must contain exactly these three functions. The system expects them. You can add anything else on top of them.

2.1 user_setup()

cpp
void user_setup() {
    // Called ONCE when Core 1 starts
    // Use for: pinMode(), library init, variable defaults
}

Purpose: Initialize your hardware. This runs once after boot.

What to do here:

  • Call pinMode(pin, OUTPUT) or pinMode(pin, INPUT) for your GPIOs
  • Initialize libraries: FastLED.addLeds(...), servo.attach(pin), Wire.begin()
  • Set initial states: digitalWrite(pin, LOW), variables to zero

Example:

cpp
#include <FastLED.h>
#include <ESP32Servo.h>

#define PIN_DRIVE       5
#define PIN_STEER       6
#define PIN_HEADLIGHTS  12
#define PIN_LEDS        48
#define NUM_LEDS        30

CRGB leds[NUM_LEDS];
Servo turretServo;
bool headlightsOn = false;

void user_setup() {
    // Motor pins
    pinMode(PIN_DRIVE, OUTPUT);
    pinMode(PIN_STEER, OUTPUT);

    // Headlights
    pinMode(PIN_HEADLIGHTS, OUTPUT);
    digitalWrite(PIN_HEADLIGHTS, LOW);

    // LED strip
    FastLED.addLeds<WS2812, PIN_LEDS, GRB>(leds, NUM_LEDS);
    FastLED.setBrightness(80);

    // Servo
    turretServo.attach(9);
    turretServo.write(90);  // center position
}

2.2 user_loop()

cpp
void user_loop() {
    // Called REPEATEDLY in an infinite loop on Core 1
    // Use for: animations, sensor reads, timed events, PID
}

Purpose: Continuous logic. Runs thousands of times per second.

CRITICAL RULES:

  • NEVER use delay() — it blocks the entire Core 1 and can interfere with command processing
  • Use millis() for all timing (see examples below)
  • Keep each iteration fast (< 10ms ideally)

Example — LED Animation + Periodic Sensor Read:

cpp
unsigned long lastSensorRead = 0;
uint8_t hue = 0;

void user_loop() {
    // Rainbow animation — runs every loop
    for (int i = 0; i < NUM_LEDS; i++) {
        leds[i] = CHSV(hue + (i * 10), 255, 255);
    }
    hue++;
    FastLED.show();

    // Read temperature sensor every 2 seconds (non-blocking)
    if (millis() - lastSensorRead > 2000) {
        lastSensorRead = millis();
        float temp = readTemperature();  // your function
        // do something with temp
    }
}

Example — Non-Blocking Timing (replacing delay):

cpp
// ❌ BAD — blocks everything for 1 second:
void user_loop() {
    digitalWrite(LED, HIGH);
    delay(1000);             // NEVER DO THIS
    digitalWrite(LED, LOW);
    delay(1000);             // NEVER DO THIS
}

// ✅ GOOD — non-blocking blink:
unsigned long lastBlink = 0;
bool ledState = false;

void user_loop() {
    if (millis() - lastBlink > 1000) {
        lastBlink = millis();
        ledState = !ledState;
        digitalWrite(LED, ledState);
    }
}

2.3 deck()

cpp
void deck(String type, String name, int value) {
    // Called by the SYSTEM when Deck Control sends a command
    // type:  "AXIS" or "BUTTON"
    // name:  the control name you configured (e.g., "Drive", "Headlights")
    // value: AXIS → -100 to +100 / BUTTON → 0 or 1
}

Purpose: React to real-time commands from Deck Control.

Parameters:

ParameterTypeValuesMeaning
typeString"AXIS" or "BUTTON"What kind of control sent the command
nameStringThe name you gave in Step 2Which specific control (e.g., "Drive", "Horn")
valueint-100 to +100 (axis) or 0/1 (button)The control's current value

How value works for axes:

Joystick fully back / Key "S":     value = -100
Joystick centered / No key:        value = 0
Joystick fully forward / Key "W":  value = +100
Joystick halfway forward:          value = 50

How value works for buttons:

Momentary mode:
  Key pressed:    value = 1
  Key released:   value = 0

Toggle mode:
  First click:    value = 1
  Second click:   value = 0

Full Example:

cpp
void deck(String type, String name, int value) {

    // ─── AXES ─────────────────────────────────────
    if (type == "AXIS") {

        if (name == "Drive") {
            // value: -100 (full reverse) to +100 (full forward)
            if (value > 0) {
                // Forward
                digitalWrite(IN1, HIGH);
                digitalWrite(IN2, LOW);
                analogWrite(PIN_DRIVE, map(value, 0, 100, 0, 255));
            } else if (value < 0) {
                // Reverse
                digitalWrite(IN1, LOW);
                digitalWrite(IN2, HIGH);
                analogWrite(PIN_DRIVE, map(abs(value), 0, 100, 0, 255));
            } else {
                // Stop
                digitalWrite(IN1, LOW);
                digitalWrite(IN2, LOW);
                analogWrite(PIN_DRIVE, 0);
            }
        }

        if (name == "Steering") {
            // Map -100..+100 to servo angle 0..180
            int angle = map(value, -100, 100, 0, 180);
            turretServo.write(angle);
        }
    }

    // ─── BUTTONS ──────────────────────────────────
    if (type == "BUTTON") {

        if (name == "Headlights") {
            // value: 1 = ON, 0 = OFF (toggle mode)
            headlightsOn = (value == 1);
            digitalWrite(PIN_HEADLIGHTS, headlightsOn ? HIGH : LOW);
        }

        if (name == "Horn") {
            // value: 1 = pressed, 0 = released (momentary mode)
            digitalWrite(PIN_HORN, value ? HIGH : LOW);
        }

        if (name == "Boost") {
            // Momentary: while held, increase speed by 50%
            boostActive = (value == 1);
        }
    }
}

3. Your Code Structure

Minimal Valid Code

cpp
void user_setup() {
    // empty is fine
}

void user_loop() {
    // empty is fine
}

void deck(String type, String name, int value) {
    // empty is fine — robot just ignores all commands
}

Full Typical Structure

cpp
// ═══════════════════════════════════════════
//  INCLUDES
// ═══════════════════════════════════════════
#include <FastLED.h>
#include <ESP32Servo.h>

// ═══════════════════════════════════════════
//  PIN DEFINITIONS (from Step 2)
// ═══════════════════════════════════════════
#define PIN_MOTOR_A_PWM     5
#define PIN_MOTOR_A_DIR1    6
#define PIN_MOTOR_A_DIR2    7
#define PIN_MOTOR_B_PWM     8
#define PIN_MOTOR_B_DIR1    9
#define PIN_MOTOR_B_DIR2    10
#define PIN_HEADLIGHTS      12
#define PIN_HORN             13
#define PIN_LED_STRIP        48
#define NUM_LEDS             30

// ═══════════════════════════════════════════
//  GLOBAL STATE
// ═══════════════════════════════════════════
CRGB leds[NUM_LEDS];
Servo cameraServo;

bool headlightsOn = false;
bool boostActive = false;
int currentSpeed = 0;
int currentSteering = 0;

unsigned long lastStatusReport = 0;
uint8_t animHue = 0;

// ═══════════════════════════════════════════
//  HELPER FUNCTIONS (yours — add as many as you want)
// ═══════════════════════════════════════════
void setMotorA(int speed) {
    // speed: -255 to +255
    if (speed > 0) {
        digitalWrite(PIN_MOTOR_A_DIR1, HIGH);
        digitalWrite(PIN_MOTOR_A_DIR2, LOW);
        analogWrite(PIN_MOTOR_A_PWM, speed);
    } else if (speed < 0) {
        digitalWrite(PIN_MOTOR_A_DIR1, LOW);
        digitalWrite(PIN_MOTOR_A_DIR2, HIGH);
        analogWrite(PIN_MOTOR_A_PWM, -speed);
    } else {
        digitalWrite(PIN_MOTOR_A_DIR1, LOW);
        digitalWrite(PIN_MOTOR_A_DIR2, LOW);
        analogWrite(PIN_MOTOR_A_PWM, 0);
    }
}

void setMotorB(int speed) {
    // same pattern as Motor A
    if (speed > 0) {
        digitalWrite(PIN_MOTOR_B_DIR1, HIGH);
        digitalWrite(PIN_MOTOR_B_DIR2, LOW);
        analogWrite(PIN_MOTOR_B_PWM, speed);
    } else if (speed < 0) {
        digitalWrite(PIN_MOTOR_B_DIR1, LOW);
        digitalWrite(PIN_MOTOR_B_DIR2, HIGH);
        analogWrite(PIN_MOTOR_B_PWM, -speed);
    } else {
        digitalWrite(PIN_MOTOR_B_DIR1, LOW);
        digitalWrite(PIN_MOTOR_B_DIR2, LOW);
        analogWrite(PIN_MOTOR_B_PWM, 0);
    }
}

void runLedAnimation() {
    for (int i = 0; i < NUM_LEDS; i++) {
        leds[i] = CHSV(animHue + (i * 8), 255, headlightsOn ? 255 : 40);
    }
    animHue++;
    FastLED.show();
}

// ═══════════════════════════════════════════
//  user_setup() — CALLED ONCE
// ═══════════════════════════════════════════
void user_setup() {
    // Motors
    pinMode(PIN_MOTOR_A_PWM, OUTPUT);
    pinMode(PIN_MOTOR_A_DIR1, OUTPUT);
    pinMode(PIN_MOTOR_A_DIR2, OUTPUT);
    pinMode(PIN_MOTOR_B_PWM, OUTPUT);
    pinMode(PIN_MOTOR_B_DIR1, OUTPUT);
    pinMode(PIN_MOTOR_B_DIR2, OUTPUT);

    // Headlights & Horn
    pinMode(PIN_HEADLIGHTS, OUTPUT);
    pinMode(PIN_HORN, OUTPUT);

    // LED strip
    FastLED.addLeds<WS2812, PIN_LED_STRIP, GRB>(leds, NUM_LEDS);
    FastLED.setBrightness(120);

    // Servo
    cameraServo.attach(14);
    cameraServo.write(90);
}

// ═══════════════════════════════════════════
//  user_loop() — RUNS CONTINUOUSLY
// ═══════════════════════════════════════════
void user_loop() {
    // LED animations (always running)
    runLedAnimation();

    // Periodic status report every 5 minutes
    if (millis() - lastStatusReport > 300000) {
        lastStatusReport = millis();
        // juvantia_send("Status: OK");  // Phase 2 API
    }
}

// ═══════════════════════════════════════════
//  deck() — COMMAND HANDLER FROM DECK CONTROL
// ═══════════════════════════════════════════
void deck(String type, String name, int value) {

    if (type == "AXIS") {

        if (name == "Drive") {
            currentSpeed = value;
            int multiplier = boostActive ? 2 : 1;
            int pwm = map(value, -100, 100, -255, 255) * multiplier;
            pwm = constrain(pwm, -255, 255);
            // Differential steering: both motors for drive
            setMotorA(pwm);
            setMotorB(pwm);
        }

        if (name == "Steering") {
            currentSteering = value;
            // Differential: slow down one side
            int base = map(currentSpeed, -100, 100, -255, 255);
            int steerFactor = map(value, -100, 100, -128, 128);
            setMotorA(constrain(base + steerFactor, -255, 255));
            setMotorB(constrain(base - steerFactor, -255, 255));
        }

        if (name == "CameraPan") {
            int angle = map(value, -100, 100, 0, 180);
            cameraServo.write(angle);
        }
    }

    if (type == "BUTTON") {

        if (name == "Headlights") {
            headlightsOn = (value == 1);
            digitalWrite(PIN_HEADLIGHTS, headlightsOn ? HIGH : LOW);
            // LED strip brightness also changes (see user_loop)
        }

        if (name == "Horn") {
            digitalWrite(PIN_HORN, value ? HIGH : LOW);
        }

        if (name == "Boost") {
            boostActive = (value == 1);
        }
    }
}

4. Available Arduino Functions

You have full access to the Arduino framework and ESP-IDF functions. Here are the most commonly used:

Digital I/O

cpp
pinMode(pin, OUTPUT);           // Set pin as output
pinMode(pin, INPUT);            // Set pin as input
pinMode(pin, INPUT_PULLUP);     // Set pin as input with internal pull-up resistor
digitalWrite(pin, HIGH);        // Set pin to 3.3V
digitalWrite(pin, LOW);         // Set pin to 0V (ground)
int val = digitalRead(pin);     // Read pin state: HIGH or LOW

Analog / PWM

cpp
int val = analogRead(pin);      // Read analog voltage: 0–4095 (12-bit)
                                // ⚠ Use GPIO 1-10 (ADC1) — ADC2 unreliable with Wi-Fi

analogWrite(pin, duty);         // PWM output: 0–255
                                // Works on ANY GPIO

// Advanced PWM (higher resolution):
ledcAttach(pin, freq, resolution);     // e.g., ledcAttach(5, 5000, 8)
ledcWrite(pin, duty);                  // duty: 0 to (2^resolution - 1)

Timing

cpp
unsigned long now = millis();   // Milliseconds since boot (unsigned long)
unsigned long us = micros();    // Microseconds since boot

delayMicroseconds(10);          // OK for very short pauses (< 100µs)
// ❌ delay(ms) — NEVER USE IN user_loop() or deck()

Math

cpp
int mapped = map(value, fromLow, fromHigh, toLow, toHigh);
// Example: map(75, -100, 100, 0, 255) → 221

int limited = constrain(value, min, max);
// Example: constrain(300, 0, 255) → 255

int absolute = abs(value);
// Example: abs(-75) → 75

float s = sin(radians(angle));
float c = cos(radians(angle));

int r = random(min, max);
// Example: random(0, 100) → random number 0-99

Serial (Debug Output)

cpp
Serial.begin(115200);           // In user_setup()
Serial.println("Hello!");       // Print to serial monitor
Serial.printf("Speed: %d\n", speed);  // Formatted output

💡 Serial output is useful for debugging via USB. It will appear in PlatformIO Serial Monitor.

String Operations

cpp
String msg = "Hello " + String(42);  // "Hello 42"
int len = msg.length();              // 8
bool eq = (name == "Drive");         // String comparison
char c = msg.charAt(0);             // 'H'
int idx = msg.indexOf("lo");        // 3
String sub = msg.substring(0, 5);   // "Hello"

5. Using Arduino Libraries

How to Add Libraries

In Step 3 of the Fabrica UI, there is a "Libraries" section above the code editor. You can:

  1. Click popular library buttons (FastLED, Servo, etc.) — one click adds them
  2. Type a custom library name in the format owner/library (PlatformIO registry format)
  3. Libraries are added to platformio.ini as lib_deps at compile time

FastLED — Addressable LED Control

Library: fastled/FastLED
cpp
#include <FastLED.h>

#define PIN_LEDS    48
#define NUM_LEDS    60

CRGB leds[NUM_LEDS];

void user_setup() {
    FastLED.addLeds<WS2812, PIN_LEDS, GRB>(leds, NUM_LEDS);
    FastLED.setBrightness(100);
}

void user_loop() {
    // Solid color
    fill_solid(leds, NUM_LEDS, CRGB::Red);

    // Rainbow
    fill_rainbow(leds, NUM_LEDS, millis() / 10, 7);

    // Individual LED
    leds[0] = CRGB(255, 0, 128);

    FastLED.show();
}

ESP32Servo — Servo Motors

Library: madhephaestus/ESP32Servo
cpp
#include <ESP32Servo.h>

Servo myServo;

void user_setup() {
    myServo.attach(9);       // GPIO 9
    myServo.write(90);       // Center position (0-180)
}

void deck(String type, String name, int value) {
    if (name == "CameraPan") {
        int angle = map(value, -100, 100, 0, 180);
        myServo.write(angle);
    }
}

Adafruit NeoPixel — Alternative LED Library

Library: adafruit/Adafruit NeoPixel
cpp
#include <Adafruit_NeoPixel.h>

#define PIN_LEDS    48
#define NUM_LEDS    30

Adafruit_NeoPixel strip(NUM_LEDS, PIN_LEDS, NEO_GRB + NEO_KHZ800);

void user_setup() {
    strip.begin();
    strip.setBrightness(100);
    strip.show();
}

void user_loop() {
    // Theater chase
    static int pos = 0;
    strip.clear();
    for (int i = pos; i < NUM_LEDS; i += 3) {
        strip.setPixelColor(i, strip.Color(0, 150, 255));
    }
    strip.show();
    pos = (pos + 1) % 3;
}

ArduinoJson — JSON Parsing

Library: bblanchon/ArduinoJson
cpp
#include <ArduinoJson.h>

void processSensorData() {
    StaticJsonDocument<200> doc;
    doc["temp"] = 23.5;
    doc["humidity"] = 67;
    doc["battery"] = 85;
    
    String output;
    serializeJson(doc, output);
    // output: {"temp":23.5,"humidity":67,"battery":85}
}

PID Controller

Library: br3ttb/PID
cpp
#include <PID_v1.h>

double setpoint = 0;    // target speed
double input = 0;       // current speed (from encoder)
double output = 0;      // PWM output

PID speedPID(&input, &output, &setpoint, 2.0, 0.5, 0.1, DIRECT);

void user_setup() {
    speedPID.SetMode(AUTOMATIC);
    speedPID.SetOutputLimits(-255, 255);
}

void deck(String type, String name, int value) {
    if (name == "Drive") {
        setpoint = map(value, -100, 100, -1000, 1000);  // target RPM
    }
}

void user_loop() {
    input = readEncoder();  // your encoder reading function
    speedPID.Compute();
    setMotor(output);
}

6. System-Provided API (Phase 2)

These functions will be available in future releases. They are called from your code and communicate with Deck Control via the system WebSocket on Core 0:

FunctionDescriptionStatus
juvantia_send(String msg)Send a text message to the operator's Deck Control🔜 Planned
juvantia_log(String msg)Write to the cloud-visible log in Fabrica🔜 Planned
juvantia_sensor(String key, float val)Push a sensor reading to the Deck Control dashboard🔜 Planned
juvantia_alert(String msg)Send an urgent notification to operator🔜 Planned

Future example:

cpp
void user_loop() {
    // Report battery every 10 minutes
    if (millis() - lastBatteryReport > 600000) {
        lastBatteryReport = millis();
        float voltage = analogRead(PIN_BATTERY) * (3.3 / 4095.0) * 2.0;
        juvantia_sensor("battery_v", voltage);

        if (voltage < 3.3) {
            juvantia_alert("Low battery! " + String(voltage) + "V");
        }
    }
}

7. Common Patterns & Recipes

Pattern: Smooth Motor Control with Ramping

Don't jump from 0 to 255 instantly — it strains the motor and gears:

cpp
int currentPWM = 0;
int targetPWM = 0;

void deck(String type, String name, int value) {
    if (name == "Drive") {
        targetPWM = map(value, -100, 100, -255, 255);
    }
}

void user_loop() {
    // Smoothly ramp toward target (acceleration/deceleration)
    if (currentPWM < targetPWM) {
        currentPWM = min(currentPWM + 5, targetPWM);
    } else if (currentPWM > targetPWM) {
        currentPWM = max(currentPWM - 5, targetPWM);
    }
    setMotor(currentPWM);
}

Pattern: Emergency Stop

Cut all motors if signal lost:

cpp
unsigned long lastCommandTime = 0;
#define SIGNAL_TIMEOUT 3000  // 3 seconds

void deck(String type, String name, int value) {
    lastCommandTime = millis();  // Reset watchdog on any command
    // ... handle commands normally
}

void user_loop() {
    if (millis() - lastCommandTime > SIGNAL_TIMEOUT) {
        // No commands for 3 seconds — emergency stop
        setMotorA(0);
        setMotorB(0);
        // Optionally: flash LEDs as warning
    }
}

Pattern: Blinking LED Without delay()

cpp
unsigned long lastBlink = 0;
bool blinkState = false;

void user_loop() {
    if (millis() - lastBlink > 500) {  // 500ms = 1Hz blink
        lastBlink = millis();
        blinkState = !blinkState;
        digitalWrite(PIN_STATUS_LED, blinkState);
    }
}

Pattern: Button Triggers Animation

cpp
bool alarmActive = false;

void deck(String type, String name, int value) {
    if (name == "Alarm") {
        alarmActive = (value == 1);
    }
}

void user_loop() {
    if (alarmActive) {
        // Red-blue police flash
        bool phase = (millis() / 200) % 2;
        for (int i = 0; i < NUM_LEDS / 2; i++) {
            leds[i] = phase ? CRGB::Red : CRGB::Blue;
        }
        for (int i = NUM_LEDS / 2; i < NUM_LEDS; i++) {
            leds[i] = phase ? CRGB::Blue : CRGB::Red;
        }
        FastLED.show();
    }
}

Pattern: Differential Steering (Tank Drive)

cpp
int driveValue = 0;
int steerValue = 0;

void deck(String type, String name, int value) {
    if (type == "AXIS") {
        if (name == "Drive") driveValue = value;
        if (name == "Steering") steerValue = value;

        // Recalculate motors whenever either axis changes
        int left  = constrain(driveValue + steerValue, -100, 100);
        int right = constrain(driveValue - steerValue, -100, 100);

        setMotorA(map(left,  -100, 100, -255, 255));
        setMotorB(map(right, -100, 100, -255, 255));
    }
}

Pattern: Compass / Heading Hold

cpp
#include <Wire.h>
// ... compass library setup

float targetHeading = 0;
bool headingHoldActive = false;

void deck(String type, String name, int value) {
    if (name == "HeadingHold") {
        headingHoldActive = (value == 1);
        if (headingHoldActive) {
            targetHeading = readCompass();  // Lock current heading
        }
    }
    if (name == "Steering" && !headingHoldActive) {
        // Manual steering only when heading hold is off
        steer(value);
    }
}

void user_loop() {
    if (headingHoldActive) {
        float current = readCompass();
        float error = targetHeading - current;
        // Apply PID correction
        int correction = computePID(error);
        steer(correction);
    }
}

8. Compilation & Flashing

What Happens When You Press "Compile Firmware"

  1. Your code from the editor is sent to the Fabrica backend
  2. The backend generates a complete PlatformIO project:
    project/
    ├── platformio.ini          ← board, libs
    ├── src/
    │   ├── main.cpp            ← System code (Core 0) + your code (Core 1)
    │   └── user_code.h         ← Your code injected here
    └── lib/
        └── (auto-downloaded)
  3. platformio run compiles it into a .bin firmware file
  4. The .bin is returned to your browser

Flashing to the Board

After compilation, the next step (Step 4) uses the Web Serial API to flash the .bin directly from your browser to the ESP32-S3:

  1. Connect the board via USB-C (the UART port, not OTG)
  2. Click "Flash Firmware" in Fabrica
  3. Browser requests serial port access (you grant permission)
  4. Firmware is written to flash memory
  5. Board reboots and starts running your code

9. Debugging

Serial Monitor

Add Serial.println() statements to debug your code:

cpp
void deck(String type, String name, int value) {
    Serial.printf("[DECK] %s | %s | %d\n", type.c_str(), name.c_str(), value);
    // ... rest of your logic
}

Common Issues

SymptomLikely CauseFix
Robot doesn't respondNo Wi-Fi connectionCheck SSID/password in config
Motors don't moveWrong pin or no pinMode(OUTPUT)Verify pin number + user_setup()
Jerky movementUsing delay()Replace with millis()
LEDs flickerNot calling FastLED.show()Add to user_loop()
Servo shakesPower from ESP32 3.3VUse external 5V BEC
Analog reads are wrongUsing ADC2 with Wi-FiUse ADC1 (GPIO 1–10)
Board doesn't bootExternal load on strapping pinMove wire to non-strapping GPIO
deck() not calledCommand name mismatchCheck exact spelling from Step 2

10. Full Glossary

TermDefinition
user_setup()Function called once on Core 1 startup. Initialize hardware here.
user_loop()Function called continuously on Core 1. Put timed logic and animations here.
deck()Function called when Deck Control sends a command. Receives type, name, and value.
type"AXIS" (analog, -100 to +100) or "BUTTON" (digital, 0 or 1)
nameThe exact string name of the control as defined in Step 2 (e.g., "Drive")
valueAxis: -100 to +100. Button: 0 (OFF) or 1 (ON)
millis()Returns milliseconds since boot. Use for all timing.
map(v, a, b, c, d)Linearly maps value v from range [a,b] to range [c,d]
constrain(v, min, max)Clamps value v between min and max
analogWrite(pin, duty)PWM output, duty 0–255
analogRead(pin)Read analog voltage, returns 0–4095 (12-bit)
pinMode(pin, mode)Configure pin: INPUT, OUTPUT, or INPUT_PULLUP
digitalWrite(pin, val)Set digital output: HIGH (3.3V) or LOW (0V)
digitalRead(pin)Read digital input: HIGH or LOW
delay()FORBIDDEN — blocks the CPU core
FastLED.show()Push LED data to the strip (FastLED library)
servo.write(angle)Move servo to angle 0–180 (Servo library)
PlatformIOBuild system used to compile the firmware
lib_depsLibraries listed in platformio.ini — auto-downloaded
Core 0System CPU core — Wi-Fi, WebSocket, out of your control
Core 1User CPU core — your code runs here
Deck ControlWeb control interface at deck.juvantia.org — joysticks, buttons, camera, telemetry
FabricaWeb portal where you configure, code, compile, and flash

Operated by Juvantia Foundation