Back to Notebook Index

Week 13: Interface Week

This week was interface week! I wanted to experiment with three.js, WebBluetooth, and the ESP32 in service of my Final Project - light-up juggling balls. The idea is to have a simluator that lets you test out light patterns before uploading them to the juggling balls.

See the interface here: Juggling Light Simluator Interface and corresponding ESP32 Arduino code.

dat.gui was nice to work with. I created menu items I could use to trigger a WebBluetooth connection.

I was able to have a button that would trigger the bluetooth device selector list. In my lab, that list was hundreds of items long. Unwieldy!


  var scene = new THREE.Scene();
  var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

  var geometry = new THREE.SphereGeometry(.4, 32, 32);
  var material = new THREE.MeshPhongMaterial( { color: 0x156289, emissive: 0x072534, side: THREE.DoubleSide, flatShading: true } );
  var material1 = new THREE.MeshPhongMaterial( { color: 0x156289, emissive: 0x072534, side: THREE.DoubleSide, flatShading: true } );
  var material2 = new THREE.MeshPhongMaterial( { color: 0x156289, emissive: 0x072534, side: THREE.DoubleSide, flatShading: true } );

  const gravity = -0.001;
  let balls = [
    new THREE.Mesh(geometry, material),
    new THREE.Mesh(geometry, material1),
    new THREE.Mesh(geometry, material2)
  ];

  balls[0].position.x = 1;
  balls[0].position.y = 0;
  balls[0].velocity = {x: 0, y: 0};
  balls[1].position.x = -1;
  balls[1].position.y = 0;
  balls[1].velocity = {x: 0, y: 0};
  balls[2].position.x = 1.8;
  balls[2].position.y = 0;
  balls[2].velocity = {x: -.01, y: 0};

  balls.forEach((o) => scene.add(o))

  let characteristic;

  const datGui = new dat.GUI({autoPlace: true});
  datGui.domElement.id = 'gui'
  datGui.add({throw1: () => throwBall(balls[0])}, 'throw1')
  datGui.add({connect: () => {
      navigator.bluetooth.requestDevice({ acceptAllDevices: true, optionalServices: ['4fafc201-1fb5-459e-8fcc-c5c9c331914b'] })
        .then(device => device.gatt.connect())
        .then(server => server.getPrimaryService('4fafc201-1fb5-459e-8fcc-c5c9c331914b'))
        .then(service => service.getCharacteristic('beb5483e-36e1-4688-b7f5-ea07361b26a8'))
        .then(characteristic => {
          // Writing 1 is the signal to reset energy expended.
          var resetEnergyExpended = Uint8Array.of(1);
          return characteristic.writeValue(resetEnergyExpended);
        })
        .then(_ => {
          console.log('Energy expended has been reset.');
        })
        .catch(error => { console.log(error); });
  }}, 'connect')
  datGui.add({upload: () => throwBall(balls[0])}, 'upload')

  camera.position.z = 5;
  var renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  let currentBall = 2;

  function throwBall() {
    currentBall = (currentBall + 1) % 3
    balls[currentBall].velocity.y = .1;
    let isRightHand = balls[currentBall].position.x > 0;
    let throwSpeedX = .01;
    balls[currentBall].velocity.x = isRightHand ? -throwSpeedX : throwSpeedX;
  }

  function throwIfClose() {
    if (balls[currentBall].position.y < -1.5 && balls[currentBall].velocity.y < 0) {
      throwBall()
    }
  }
  var controls = new THREE.OrbitControls( camera, renderer.domElement );
  controls.autoRotate = true;
  controls.autoRotateSpeed = 5;

  function animate() {
    requestAnimationFrame(animate);
    throwIfClose();
    controls.update();
    balls.forEach((o) => {
      if (o.position.y > 1) {
        o.material.color.setHex(0xff0000); // there is also setHSV and setRGB
        o.material.emissive.setHex(0xff0000); // there is also setHSV and setRGB
      } else {
        o.material.color.setHex(0x156289); // there is also setHSV and setRGB
        o.material.emissive.setHex(0x072534); // there is also setHSV and setRGB
      }
      if (o.position.y <= -2 && o.velocity.y <= 0) {
        o.velocity.y = 0;
        o.velocity.x = 0;
        o.velocity.x = 0;
        o.position.y = -2;
        o.material.color.setHex(0xFF00FF); // there is also setHSV and setRGB
        o.material.emissive.setHex(0xFF00FF); // there is also setHSV and setRGB
      } else {
        o.velocity.y += gravity;
      }
      o.position.x += o.velocity.x;
      o.position.y += o.velocity.y;
      o.rotation.x += 0.01;
      o.rotation.y += 0.01;
    });

    renderer.render(scene, camera);
  }

  animate();