Product Documentation

Guides and examples for CubeLab hardware, the browser simulation, and the secure console environment.

/

CubeLab

Copy linkUpdated Sep 2025

The CubeLab is a 4×4×4 LED matrix controlled by a microcontroller. It’s typically powered by a single-cell 3.7 V battery (with a regulated 5 V rail for logic, if required) and consists of 64 LEDs arranged in 4 layers of 16 columns.

Note: Pin maps vary by board (Arduino vs. ESP32). Treat the mappings below as a template—verify your exact pins.
  • Layers (example): [A0, A1, A2, A3]
  • Columns (example): [A4, A5, RX(D0), TX(D1), D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13]

Diagram of CubeLab wiring & connections:

CubeLab Diagram

Steps to Operate the CubeLab

  1. Install the Arduino IDE. Download it from the official site and follow the guide for your OS.
  2. Set up the CubeLab.
    • Kit: Watch the assembly video; ensure all connections match your pin map.
    • Finished unit: Proceed to connect to your microcontroller/USB and verify power.
  3. Write code. Open the Arduino IDE and create your sketch. See examples below.
  4. Upload code. Select the correct board & port, then upload.
  5. Power the CubeLab. Use a charged 3.7 V battery (with proper regulation for your board as needed).

Examples (Arduino / C++)

Tip: use arrays for pin maps rather than numeric ranges so you don’t depend on implicit numbering of A/D pins.

Setup: define layers & columns
cpp
// Adjust pins to your board:
// Note TX is D1; if you include TX, don't duplicate D1 again.
const int layers[4]  = {A0, A1, A2, A3};
const int columns[16]= {A4, A5, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};

void setup() {
  for (int i = 0; i < 4;  i++) pinMode(layers[i], OUTPUT);
  for (int i = 0; i < 16; i++) pinMode(columns[i], OUTPUT);
  // Default OFF: columns LOW? depends on your wiring (common anode/cathode).
  // Set a known state here:
  for (int i = 0; i < 16; i++) digitalWrite(columns[i], LOW);
  for (int i = 0; i < 4;  i++) digitalWrite(layers[i], HIGH); // e.g., HIGH = layer off
}
Turn on a single LED
cpp
// Example assumes: column HIGH turns LED on, layer LOW selects layer (inverted).
void loop() {
  int col = columns[0]; // pick a column
  int lay = layers[0];  // pick a layer

  digitalWrite(col, HIGH); // turn on column
  digitalWrite(lay, LOW);  // enable layer
  delay(500);

  // clear
  digitalWrite(col, LOW);
  digitalWrite(lay, HIGH);
  delay(500);
}
Blink a single LED
cpp
void loop() {
  int col = columns[1];
  int lay = layers[0];

  // on
  digitalWrite(col, HIGH);
  digitalWrite(lay, LOW);
  delay(500);

  // off
  digitalWrite(col, LOW);
  digitalWrite(lay, HIGH);
  delay(500);
}
Light an entire layer
cpp
void loop() {
  int lay = layers[0];

  // turn all columns on
  for (int i = 0; i < 16; i++) digitalWrite(columns[i], HIGH);
  digitalWrite(lay, LOW);
  delay(600);

  // clear
  for (int i = 0; i < 16; i++) digitalWrite(columns[i], LOW);
  digitalWrite(lay, HIGH);
  delay(400);
}
Cycle layers (basic multiplex)
cpp
void loop() {
  for (int l = 0; l < 4; l++) {
    // Turn on some pattern (here: all columns)
    for (int i = 0; i < 16; i++) digitalWrite(columns[i], HIGH);

    digitalWrite(layers[l], LOW);   // enable layer
    delay(3);                       // small persistence
    digitalWrite(layers[l], HIGH);  // disable

    // Clear columns if needed for next step
    for (int i = 0; i < 16; i++) digitalWrite(columns[i], LOW);
  }
}
Diagonal sweep (demo)
cpp
void loop() {
  // naive demo; adapt to your physical mapping (x,y,z -> index in columns[] & layers[])
  for (int i = 0; i < 4; i++) {
    int col = columns[i]; // diagonal "column"
    int lay = layers[i];

    digitalWrite(col, HIGH);
    digitalWrite(lay, LOW);
    delay(200);
    digitalWrite(col, LOW);
    digitalWrite(lay, HIGH);
  }
}

Simulation

Copy linkUpdated Sep 2025

LED Cube Simulation

Design and test Arduino-style sketches for a 4×4×4 LED cube directly in the browser. Pick a product (pin map), choose a color with the picker, paste a sketch, and run it live on the 3D cube.

Shortcuts: Ctrl/⌘ + Enter Run · Esc Stop
Pin maps: Use the product selector to switch between boards. The Pin Map card shows layers/columns you can click to ping in 3D.
Stop behavior: Stop cancels delays immediately and halts loop() on the next yield.

Sketch API (C++ like)

  • void setup() — runs once.
  • void loop() — repeats until you press Stop.
  • pinMode(PIN, MODE) — accepted (no-op in sim, for compatibility).
  • digitalWrite(PIN, HIGH|LOW) — toggles a layer pin (e.g. A0..A3) or a column pin (depends on product pin map).
  • delay(ms) — pauses; cancellable by Stop.
  • Helpers: layerOn/Off(y), columnOn/Off(x,z), ledOn/Off(x,y,z), setColor("#hex") (optional; the color picker also sets LED color).

Coordinates are 0…3. Columns are addressed by (x,z), layers by y. The exact pin names for columns vary by product; see the Pin Map on the simulation page. Multiple statements on one line are supported (;), but one per line is clearer.

Examples (paste into the editor)
Minimal starter
cpp
void setup() {
  // quick layer pulse
  digitalWrite(A0, HIGH);
  delay(200);
  digitalWrite(A0, LOW);
}

void loop() {
  // blink one voxel
  ledOn(1, 1, 1);
  delay(200);
  ledOff(1, 1, 1);
  delay(200);
}
Orbit Glow (perimeter spin + layer scan)
cpp
// Keep all layers on for a tall beam
void setup() {
  layerOn(0);
  layerOn(1);
  layerOn(2);
  layerOn(3);
}

void loop() {
  // Perimeter spin (clockwise)
  columnOn(0, 0); delay(90); columnOff(0, 0);
  columnOn(1, 0); delay(90); columnOff(1, 0);
  columnOn(2, 0); delay(90); columnOff(2, 0);
  columnOn(3, 0); delay(90); columnOff(3, 0);
  columnOn(3, 1); delay(90); columnOff(3, 1);
  columnOn(3, 2); delay(90); columnOff(3, 2);
  columnOn(3, 3); delay(90); columnOff(3, 3);
  columnOn(2, 3); delay(90); columnOff(2, 3);
  columnOn(1, 3); delay(90); columnOff(1, 3);
  columnOn(0, 3); delay(90); columnOff(0, 3);
  columnOn(0, 2); delay(90); columnOff(0, 2);
  columnOn(0, 1); delay(90); columnOff(0, 1);

  // Layer scan with all columns on
  columnOn(0,0); columnOn(1,0); columnOn(2,0); columnOn(3,0);
  columnOn(0,1); columnOn(1,1); columnOn(2,1); columnOn(3,1);
  columnOn(0,2); columnOn(1,2); columnOn(2,2); columnOn(3,2);
  columnOn(0,3); columnOn(1,3); columnOn(2,3); columnOn(3,3);

  layerOn(0); delay(120); layerOff(0);
  layerOn(1); delay(120); layerOff(1);
  layerOn(2); delay(120); layerOff(2);
  layerOn(3); delay(150); layerOff(3);

  columnOff(0,0); columnOff(1,0); columnOff(2,0); columnOff(3,0);
  columnOff(0,1); columnOff(1,1); columnOff(2,1); columnOff(3,1);
  columnOff(0,2); columnOff(1,2); columnOff(2,2); columnOff(3,2);
  columnOff(0,3); columnOff(1,3); columnOff(2,3); columnOff(3,3);

  delay(120);
}
Comet Rain (falling droplets)
cpp
void setup() {
  layerOff(0); layerOff(1); layerOff(2); layerOff(3);
}

void loop() {
  // Drop at (x=1,z=2)
  columnOn(1,2); layerOn(3); delay(90); layerOff(3); columnOff(1,2);
  columnOn(1,2); layerOn(2); delay(90); layerOff(2); columnOff(1,2);
  columnOn(1,2); layerOn(1); delay(90); layerOff(1); columnOff(1,2);
  columnOn(1,2); layerOn(0); delay(120); layerOff(0); columnOff(1,2);

  // Drop at (x=2,z=1)
  columnOn(2,1); layerOn(3); delay(90); layerOff(3); columnOff(2,1);
  columnOn(2,1); layerOn(2); delay(90); layerOff(2); columnOff(2,1);
  columnOn(2,1); layerOn(1); delay(90); layerOff(1); columnOff(2,1);
  columnOn(2,1); layerOn(0); delay(120); layerOff(0); columnOff(2,1);

  // Drop at (x=0,z=3)
  columnOn(0,3); layerOn(3); delay(90); layerOff(3); columnOff(0,3);
  columnOn(0,3); layerOn(2); delay(90); layerOff(2); columnOff(0,3);
  columnOn(0,3); layerOn(1); delay(90); layerOff(1); columnOff(0,3);
  columnOn(0,3); layerOn(0); delay(120); layerOff(0); columnOff(0,3);

  delay(120);
}

Secure Console & Tailscale

Copy linkUpdated Sep 2025

Secure Console Environment

The Secure Console lets you open a temporary, authenticated web terminal to shared lab devices (Kria, Jetson, Coral) without exposing them to the public internet. Under the hood, the devices live on a Tailscale network so teammates can also use normal ssh from their own machines.

Safety: Commands run directly on the hardware. Avoid destructive operations (rm -rf, random reboot, etc.) unless you know what you are doing. Sessions may be logged.

Using the browser Secure Console

  1. Get access
    Your instructor / project owner will:
    • Issue you a device console URL (for example: a link from the Devices page).
    • Optionally share the current username/password for SSH and the web terminal.
  2. Open the Devices page
    Go to /devices in the Cube Laboratory site and pick a device card (e.g. Kria KV260).
  3. Launch the Secure Console
    Click "Open web terminal". A new page will open with a full-screen xterm.js terminal.
  4. Log in to the device
    Use the credentials provided by the owner. For our Kria setup, a common example is:
    Username: root
    Password: tubuntu

    These may change for security reasons—always follow your instructor's latest instructions.

  5. Work inside the session
    Use the terminal like a normal Linux shell: run htop, check logs, start services, etc. When you close the browser tab or your token expires, access is revoked.

Tailscale setup for SSH (project teammates)

If you're part of the project team, you can also connect directly via ssh over Tailscale. This gives you a familiar terminal in your own terminal app (VS Code, iTerm, Windows Terminal, etc.).

  1. Install Tailscale on your laptop/desktop
    • Go to the Tailscale download page and install for your OS.
    • Launch Tailscale and log in with your school or GitHub/Google account.
  2. Join the project tailnet

    Your project owner will send you an invite link to the tailnet used for the Kria/Jetson/Coral devices. Click the link and accept the invite. You should now see your machine as "Active" in the Tailscale UI.

  3. Ensure the board is online in Tailscale

    On the owner's side, the Kria (or other board) runs the Tailscale client and appears with a name like kria-lab or a 100.x.y.z Tailscale IP address. You do not need a public IP; Tailscale handles the secure tunnel.

  4. Connect via SSH

    From your local terminal, use ssh to the Tailscale hostname or IP:

    SSH over Tailscale
    bash
    # Example: using Tailscale hostname
    ssh root@kria-lab    # or ubuntu@kria-lab, depending on the device user
    
    # Example: using Tailscale IP
    ssh root@100.x.y.z   # replace with the board's Tailscale IP

    If Tailscale SSH is enabled in the admin console, you may not need to manage SSH keys manually—Tailscale can authorize based on your login.

  5. Keep the environment healthy
    • Coordinate reboots or heavy changes with the rest of the team.
    • Don't change network or Tailscale configuration on the device unless you own it.
    • Log out when you are done (exit in SSH, close the browser tab for the Secure Console).