← Home
Docs/Software/Web Bluetooth & Dashboard

Web Bluetooth & Dashboard Recommendations

Building a wireless dashboard is completely optional. During practice and testing, you can use the USB Serial Monitor, a generic phone BLE terminal app, or build your own custom web page.

If you want to build a quick web interface, the Web Bluetooth API (supported in Google Chrome and Microsoft Edge on desktop, laptops, and Android) is convenient because it runs directly in the browser with no native app installs.

Here are some recommendations and example patterns for how you can structure wireless communication.


1. Protocol Ideas & Recommendations

You don't need to follow any rigid format. Design whatever feels easiest for your team to write and debug:

Approach A: Plain Text or CSV (Simplest)

Just send comma-separated values or human-readable strings from the ESP32:

text
1.42, -0.4, 12.15

Or:

text
dist: 1.42m, heading: -0.4 deg, batt: 12.1V

This is effortless to view in the Arduino Serial Monitor and quick to split in JavaScript using string.split(",").

Approach B: Tagged Key-Value Tokens (Flexible)

If you want to stream multiple telemetry points at once, prefixing each value with a short key makes parsing easy:

text
D:1.420 H:-0.4 B:12.15

In your frontend, you can split by spaces and extract each value by its prefix:

  • D: = Current distance in meters
  • H: = Current heading angle in degrees
  • B: = Battery voltage

Approach C: Single-Letter or Prefix Commands

For sending data from the computer/phone to the car:

  • Single-letter actions: S for Start, X for Stop, R for Reset, A for Arm motors.
  • Simple value updates: DIST:1.50 or TIME:8.0.

2. Example Web Bluetooth Workflow

Below is a minimal JavaScript template showing how to connect to a BLE peripheral, receive incoming notifications, and send a command string. You can adapt this into your own custom UI.

Connecting to the Device

js
const SERVICE_UUID = "19b10000-e8f2-537e-4f6c-d104768a1214";
const STATUS_CHAR  = "19b10001-e8f2-537e-4f6c-d104768a1214";
const CMD_CHAR     = "19b10002-e8f2-537e-4f6c-d104768a1214";

let bleDevice = null;
let cmdChar = null;

async function connectToCar() {
  try {
    // 1. Prompt user to choose the Bluetooth device
    bleDevice = await navigator.bluetooth.requestDevice({
      filters: [{ namePrefix: "Scioly" }],
      optionalServices: [SERVICE_UUID],
    });

    bleDevice.addEventListener("gattserverdisconnected", () => {
      cmdChar = null;
      console.log("Car disconnected");
    });

    // 2. Connect to the GATT server
    const server = await bleDevice.gatt.connect();
    const service = await server.getPrimaryService(SERVICE_UUID);

    // 3. Listen for notifications from the car
    const statusChar = await service.getCharacteristic(STATUS_CHAR);
    await statusChar.startNotifications();
    statusChar.addEventListener("characteristicvaluechanged", handleIncomingData);

    // 4. Get the characteristic used for sending commands
    cmdChar = await service.getCharacteristic(CMD_CHAR);

    console.log("Connected to car!");
  } catch (err) {
    console.error("Connection failed:", err);
  }
}

Reading Incoming Telemetry

js
function handleIncomingData(event) {
  // Decode raw bytes to text
  const message = new TextDecoder().decode(event.target.value).trim();
  console.log("Received from car:", message);

  // Example: Split space-separated tokens
  const tokens = message.split(" ");
  tokens.forEach((token) => {
    const [key, value] = token.split(":");
    if (key === "D") {
      console.log("Distance:", parseFloat(value), "meters");
    } else if (key === "H") {
      console.log("Heading:", parseFloat(value), "degrees");
    } else if (key === "B") {
      console.log("Battery:", parseFloat(value), "V");
    }
  });
}

Sending Commands to the Car

js
async function sendCommand(commandText) {
  if (!cmdChar) {
    console.warn("Not connected to car");
    return;
  }

  // Encode string into bytes and write to the command characteristic
  const data = new TextEncoder().encode(commandText.trim());
  await cmdChar.writeValue(data);
  console.log("Sent command:", commandText);
}

// Example usage:
// sendCommand("START");
// sendCommand("DIST:2.00");

3. General Tips

  • Use what works best for your team: You don't need fancy graphs or full web apps. Even a basic HTML page with a "Connect" button, two input boxes for distance/time, and a "Send" button is enough to save hours of pit tuning.
  • Serial Monitor Fallback: Always make sure your car can still be operated via the physical start button and USB Serial cable in case you have Bluetooth issues or forget your laptop.
  • Competition Rule Reminder: Wireless communication is strictly banned during competition runs and impound. Always turn the physical wireless switch OFF (and verify the wireless LED D1 is dark) before turning your car in for inspection!