Search code examples
javascriptgoogle-chromebluetooth-lowenergy

How can I send a slider value over Bluetooth LE every time the slider changes without spamming "Transaction already in progress" errors?


I'm controlling the speed of a motor over BLE with a GUI in a website. Every time the slider changes position a new "oninput" event is triggered. This happens about 50x /sec when you're using the slider. Because you can only send one BLE transmission at a time and a BLE transmission takes ~50ms there are a lot of "A transaction is already in progress errors". This results in the last few inputs being lost. When you quickly flick the slider from left to right the stepper motor will receive that the position is somewhere in the middle. I want a solution where:

  • I can change the slider and see the stepper motor speed up and slow down before I release the slider.
  • I also want the final value before I stop sliding to be the one that the stepper motor eventually settles on.

Here's my hacky solution: On input an asynchronous method is called that tries to send the value. If it fails then it checks if it failed because there was already another transmission in progress and not some other error. If that's the case it then checks if the value it was trying to send is still the most recent value after failing to send. Only if those 2 conditions are met it will call itself and try again.

Here's the code:

document.getElementById("sldSpeed").oninput = updateSpeed;
let speed = 0;
function updateSpeed() {
    speed = document.getElementById("sldSpeed").value;
    speed = parseInt(speed * 100);
    document.getElementById("lblSpeed").innerHTML = `${speed / 100} cm/s`;
    schrijfRealTimeSpeed(speed);
};

async function schrijfRealTimeSpeed(waarde) {
    schrijfUint32Value(karDelta, waarde)
        .catch((problem) => {
            if (problem.message === "GATT operation already in progress." & waarde === speed)
                schrijfRealTimeSpeed(waarde);
        });
}

(this schrijfUint32Value(karDelta, waarde) calles a methode in a BLE module script and converts the number to a byte array and then uses the Javascript BLE function karakteristiek.writeValueWithResponse(byteArr) )

Question: I'm abusing the error system and this probably isn't efficiënt. The authors on git said that the correct way is to only send a new request once the previous request has returned a promise so you don't get the "GAAT transmission in progress" errors in the first place. Knowing this, is there a cleaner more efficiënt, better and more javascript-like way to solve this?


Solution

  • Does this work for you

    document.getElementById("sldSpeed").oninput = updateSpeed;
    let speed = 0;
    let firstrun = true;
    let lastvalue = 0;
    function updateSpeed() {
    speed = document.getElementById("sldSpeed").value;
    speed = parseInt(speed * 100);
    document.getElementById("lblSpeed").innerHTML = `${speed / 100}cm/s`;
      if (firstrun == true)
      {
        firstrun = false;
        schrijfRealTimeSpeed(speed);
      }
    
    }
    async function schrijfRealTimeSpeed(waarde) {
    
       const realtimepromise = new Promise(async (resolve, reject) => {
       if (lastvalue!= waarde)
    {
     lastvalue = waarde;
    await schrijfUint32Value(karDelta, waarde);
    resolve();
      } else
      {
      reject();
      firstrun = true;
        }
          });
      
       realtimepromise.then( (val) => schrijfRealTimeSpeed(speed));
    
       }