Sensores para a Web

Use a API Generic Sensor para acessar sensores no dispositivo, como acelerômetros, giroscópios e magnetômetros.

Alex Shalamov
Alex Shalamov
Mikhail Pozdnyakov
Mikhail Pozdnyakov

Hoje, os dados de sensores são usados em muitos aplicativos específicos da plataforma para permitir casos de uso como jogos imersivos, monitoramento de atividade física e realidade aumentada ou virtual. Não seria legal diminuir a distância entre aplicativos específicos da plataforma e da Web? Insira a API Generic Sensor para a Web.

O que é a API Generic Sensor?

A API Generic Sensor (link em inglês) é um conjunto de interfaces que expõem dispositivos de sensor à plataforma da Web. A API consiste na interface base Sensor e em um conjunto de classes de sensor concretas criadas com base nela. Ter uma interface de base simplifica o processo de implementação e especificação para as classes de sensores concretos. Por exemplo, confira a classe Gyroscope. Ele é superpequeno! A funcionalidade principal é especificada pela interface base, e Gyroscope apenas a estende com três atributos que representam a velocidade angular.

Algumas classes de sensores fazem interface com sensores de hardware reais, como as classes de acelerômetro ou giroscópio. Eles são chamados de sensores de baixo nível. Outros sensores, chamados de sensores de fusão, mesclam dados de vários sensores de baixo nível para expor informações que um script precisaria calcular. Por exemplo, o sensor AbsoluteOrientation fornece uma matriz de rotação 4x4 pronta para uso com base nos dados obtidos do acelerômetro, do giroscópio e do magnetômetro.

Você pode pensar que a plataforma da Web já fornece dados de sensores, e você está absolutamente certo! Por exemplo, os eventos DeviceMotion e DeviceOrientation expõem dados do sensor de movimento. Então, por que precisamos de uma nova API?

Em comparação com as interfaces atuais, a API Generic Sensor oferece várias vantagens:

  • A API Generic Sensor é uma estrutura de sensor que pode ser facilmente estendida com novas classes de sensor. Cada uma dessas classes mantém a interface genérica. O código do cliente escrito para um tipo de sensor pode ser reutilizado para outro com pouquíssimas modificações.
  • Você pode configurar o sensor. Por exemplo, é possível definir a frequência de amostragem adequada às necessidades do seu aplicativo.
  • É possível detectar se um sensor está disponível na plataforma.
  • As leituras de sensores têm carimbos de data/hora de alta precisão, permitindo uma melhor sincronização com outras atividades no aplicativo.
  • Os modelos de dados e sistemas de coordenadas do sensor são claramente definidos, permitindo que os fornecedores de navegadores implementem soluções interoperáveis.
  • As interfaces baseadas no Sensor genérico não estão vinculadas ao DOM (ou seja, não são objetos navigator nem window), o que abre oportunidades futuras para usar a API em service workers ou implementá-la em runtimes JavaScript sem interface gráfica, como dispositivos incorporados.
  • Os aspectos de segurança e privacidade são a principal prioridade da API Generic Sensor e oferecem uma segurança muito melhor em comparação com as APIs Sensor mais antigas. Há integração com a API Permissions.
  • A sincronização automática com coordenadas de tela está disponível para Accelerometer, Gyroscope, LinearAccelerationSensor, AbsoluteOrientationSensor, RelativeOrientationSensor e Magnetometer.

APIs de sensores genéricos disponíveis

No momento da redação deste artigo, há vários sensores que você pode testar.

Sensores de movimento:

  • Accelerometer
  • Gyroscope
  • LinearAccelerationSensor
  • AbsoluteOrientationSensor
  • RelativeOrientationSensor
  • GravitySensor

Sensores ambientais:

  • AmbientLightSensor (por trás da flag #enable-generic-sensor-extra-classes no Chromium).
  • Magnetometer (por trás da flag #enable-generic-sensor-extra-classes no Chromium).

Detecção de recursos

A detecção de recursos das APIs de hardware é complicada, já que é necessário detectar se o navegador é compatível com a interface em questão e se o dispositivo tem o sensor correspondente. É fácil verificar se o navegador é compatível com uma interface. (Substitua Accelerometer por qualquer uma das outras interfaces mencionadas acima.)

if ('Accelerometer' in window) {
  // The `Accelerometer` interface is supported by the browser.
  // Does the device have an accelerometer, though?
}

Para um resultado de detecção de recursos realmente significativo, tente se conectar ao sensor também. Este exemplo ilustra como fazer isso.

let accelerometer = null;
try {
  accelerometer = new Accelerometer({ frequency: 10 });
  accelerometer.onerror = (event) => {
    // Handle runtime errors.
    if (event.error.name === 'NotAllowedError') {
      console.log('Permission to access sensor was denied.');
    } else if (event.error.name === 'NotReadableError') {
      console.log('Cannot connect to the sensor.');
    }
  };
  accelerometer.onreading = (e) => {
    console.log(e);
  };
  accelerometer.start();
} catch (error) {
  // Handle construction errors.
  if (error.name === 'SecurityError') {
    console.log('Sensor construction was blocked by the Permissions Policy.');
  } else if (error.name === 'ReferenceError') {
    console.log('Sensor is not supported by the User Agent.');
  } else {
    throw error;
  }
}

Polyfill

Para navegadores que não são compatíveis com a API Generic Sensor, um polyfill está disponível. O polyfill permite carregar apenas as implementações dos sensores relevantes.

// Import the objects you need.
import { Gyroscope, AbsoluteOrientationSensor } from './src/motion-sensors.js';

// And they're ready for use!
const gyroscope = new Gyroscope({ frequency: 15 });
const orientation = new AbsoluteOrientationSensor({ frequency: 60 });

O que são todos esses sensores? Como posso usá-los?

Sensores é uma área que pode precisar de uma breve introdução. Se você já conhece sensores, vá direto para a seção de programação prática. Caso contrário, vamos analisar cada sensor compatível em detalhes.

Acelerômetro e sensor de aceleração linear

Medições do sensor do acelerômetro

O sensor Accelerometer mede a aceleração de um dispositivo que hospeda o sensor em três eixos (X, Y e Z). Esse sensor é um sensor inercial, ou seja, quando o dispositivo está em queda livre linear, a aceleração total medida é de 0 m/s2. Quando um dispositivo está deitado sobre uma mesa, a aceleração na direção para cima (eixo Z) é igual à gravidade da Terra, ou seja, g ≈ +9,8 m/s2, já que ele está medindo a força da mesa empurrando o dispositivo para cima. Se você empurrar o dispositivo para a direita, a aceleração no eixo X será positiva ou negativa se o dispositivo for acelerado da direita para a esquerda.

Os acelerômetros podem ser usados para contagem de passos, detecção de movimento ou orientação simples do dispositivo. Muitas vezes, as medições do acelerômetro são combinadas com dados de outras fontes para criar sensores de fusão, como sensores de orientação.

O LinearAccelerationSensor mede a aceleração aplicada ao dispositivo que hospeda o sensor, excluindo a contribuição da gravidade. Quando um dispositivo está em repouso, por exemplo, deitado sobre a mesa, o sensor mede uma aceleração de ≈ 0 m/s2 em três eixos.

Sensor de gravidade

Já é possível para os usuários derivar manualmente leituras próximas às de um sensor de gravidade inspecionando manualmente as leituras Accelerometer e LinearAccelerometer, mas isso pode ser complicado e depender da acurácia dos valores fornecidos por esses sensores. Plataformas como o Android podem fornecer leituras de gravidade como parte do sistema operacional, o que deve ser mais barato em termos de computação, fornecer valores mais precisos dependendo do hardware do usuário e ser mais fácil de usar em termos de ergonomia da API. O GravitySensor retorna o efeito da aceleração ao longo dos eixos X, Y e Z do dispositivo devido à gravidade.

Giroscópio

Medições do sensor de giroscópio

O sensor Gyroscope mede a velocidade angular em radianos por segundo em torno dos eixos X, Y e Z locais do dispositivo. A maioria dos dispositivos de consumo tem giroscópios mecânicos (MEMS), que são sensores inerciais que medem a taxa de rotação com base na força inercial de Coriolis. Os giroscópios MEMS são propensos a desvios causados pela sensibilidade gravitacional do sensor, que deforma o sistema mecânico interno dele. Os giroscópios oscilam em frequências relativamente altas, por exemplo, dezenas de kHz e, portanto, podem consumir mais energia em comparação com outros sensores.

Sensores de orientação

Medições do sensor de orientação absoluta

O AbsoluteOrientationSensor é um sensor de fusão que mede a rotação de um dispositivo em relação ao sistema de coordenadas da Terra, enquanto o RelativeOrientationSensor fornece dados que representam a rotação de um dispositivo que hospeda sensores de movimento em relação a um sistema de coordenadas de referência estacionário.

Todos os frameworks JavaScript 3D modernos são compatíveis com quaternions e matrizes de rotação para representar a rotação. No entanto, se você usar o WebGL diretamente, o OrientationSensor terá convenientemente uma propriedade quaternion e um método populateMatrix(). Confira alguns snippets:

three.js

let torusGeometry = new THREE.TorusGeometry(7, 1.6, 4, 3, 6.3);
let material = new THREE.MeshBasicMaterial({ color: 0x0071c5 });
let torus = new THREE.Mesh(torusGeometry, material);
scene.add(torus);

// Update mesh rotation using quaternion.
const sensorAbs = new AbsoluteOrientationSensor();
sensorAbs.onreading = () => torus.quaternion.fromArray(sensorAbs.quaternion);
sensorAbs.start();

// Update mesh rotation using rotation matrix.
const sensorRel = new RelativeOrientationSensor();
let rotationMatrix = new Float32Array(16);
sensor_rel.onreading = () => {
  sensorRel.populateMatrix(rotationMatrix);
  torus.matrix.fromArray(rotationMatrix);
};
sensorRel.start();

BABYLON

const mesh = new BABYLON.Mesh.CreateCylinder('mesh', 0.9, 0.3, 0.6, 9, 1, scene);
const sensorRel = new RelativeOrientationSensor({ frequency: 30 });
sensorRel.onreading = () => mesh.rotationQuaternion.FromArray(sensorRel.quaternion);
sensorRel.start();

WebGL

// Initialize sensor and update model matrix when new reading is available.
let modMatrix = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
const sensorAbs = new AbsoluteOrientationSensor({ frequency: 60 });
sensorAbs.onreading = () => sensorAbs.populateMatrix(modMatrix);
sensorAbs.start();

// Somewhere in rendering code, update vertex shader attribute for the model
gl.uniformMatrix4fv(modMatrixAttr, false, modMatrix);

Os sensores de orientação permitem vários casos de uso, como jogos imersivos, realidade aumentada e virtual.

Para mais informações sobre sensores de movimento, casos de uso avançados e requisitos, confira o documento explicação sobre sensores de movimento.

Sincronização com coordenadas de tela

Por padrão, as leituras dos sensores espaciais são resolvidas em um sistema de coordenadas local vinculado ao dispositivo e não consideram a orientação da tela.

Sistema de coordenadas do dispositivo
Sistema de coordenadas do dispositivo

No entanto, muitos casos de uso, como jogos ou realidade aumentada e virtual, exigem que as leituras do sensor sejam resolvidas em um sistema de coordenadas vinculado à orientação da tela.

Sistema de coordenadas da tela
Sistema de coordenadas da tela

Antes, o remapeamento das leituras do sensor para coordenadas da tela precisava ser implementado em JavaScript. Essa abordagem é ineficiente e aumenta significativamente a complexidade do código do aplicativo da Web. O aplicativo da Web precisa monitorar as mudanças de orientação da tela e realizar transformações de coordenadas para leituras de sensores, o que não é trivial para ângulos de Euler ou quaternions.

A API Generic Sensor oferece uma solução muito mais simples e confiável. O sistema de coordenadas local é configurável para todas as classes de sensores espaciais definidas: Accelerometer, Gyroscope, LinearAccelerationSensor, AbsoluteOrientationSensor, RelativeOrientationSensor e Magnetometer. Ao transmitir a opção referenceFrame ao construtor de objetos do sensor, o usuário define se as leituras retornadas serão resolvidas em coordenadas de dispositivo ou tela.

// Sensor readings are resolved in the Device coordinate system by default.
// Alternatively, could be RelativeOrientationSensor({referenceFrame: "device"}).
const sensorRelDevice = new RelativeOrientationSensor();

// Sensor readings are resolved in the Screen coordinate system. No manual remapping is required!
const sensorRelScreen = new RelativeOrientationSensor({ referenceFrame: 'screen' });

Vamos programar!

A API Generic Sensor é muito simples e fácil de usar. A interface Sensor tem métodos start() e stop() para controlar o estado do sensor e vários manipuladores de eventos para receber notificações sobre ativação do sensor, erros e leituras recém-disponíveis. As classes de sensores concretos geralmente adicionam atributos de leitura específicos à classe de base.

Ambiente de desenvolvimento

Durante o desenvolvimento, você poderá usar sensores com localhost. Se você estiver desenvolvendo para dispositivos móveis, configure o encaminhamento de porta para seu servidor local e pronto!

Quando o código estiver pronto, implante-o em um servidor compatível com HTTPS. As páginas do GitHub são veiculadas por HTTPS, o que as torna um ótimo lugar para compartilhar suas demonstrações.

Rotação do modelo 3D

Neste exemplo simples, usamos os dados de um sensor de orientação absoluta para modificar o quaternário de rotação de um modelo 3D. O model é uma instância da classe Object3D do three.js que tem uma propriedade quaternion. O snippet de código a seguir da demonstração orientação do smartphone ilustra como o sensor de orientação absoluta pode ser usado para girar um modelo 3D.

function initSensor() {
  sensor = new AbsoluteOrientationSensor({ frequency: 60 });
  sensor.onreading = () => model.quaternion.fromArray(sensor.quaternion);
  sensor.onerror = (event) => {
    if (event.error.name == 'NotReadableError') {
      console.log('Sensor is not available.');
    }
  };
  sensor.start();
}

A orientação do dispositivo será refletida na rotação 3D model na cena WebGL.

Os sensores atualizam a orientação do modelo 3D
O sensor atualiza a orientação de um modelo 3D

Punchmeter

O snippet de código a seguir foi extraído da demonstração do punchmeter e ilustra como o sensor de aceleração linear pode ser usado para calcular a velocidade máxima de um dispositivo, supondo que ele esteja inicialmente parado.

this.maxSpeed = 0;
this.vx = 0;
this.ax = 0;
this.t = 0;

/* … */

this.accel.onreading = () => {
  let dt = (this.accel.timestamp - this.t) * 0.001; // In seconds.
  this.vx += ((this.accel.x + this.ax) / 2) * dt;

  let speed = Math.abs(this.vx);

  if (this.maxSpeed < speed) {
    this.maxSpeed = speed;
  }

  this.t = this.accel.timestamp;
  this.ax = this.accel.x;
};

A velocidade atual é calculada como uma aproximação da integral da função de aceleração.

Aplicativo da Web de demonstração para medição da velocidade do soco.
Medição da velocidade de um soco

Depuração e substituição de sensores com o Chrome DevTools

Em alguns casos, não é necessário um dispositivo físico para usar a API Generic Sensor. O Chrome DevTools tem um ótimo suporte para simular a orientação do dispositivo.

O Chrome DevTools era usado para substituir os dados de orientação personalizada de um smartphone virtual.
Simular a orientação do dispositivo com o Chrome DevTools

Privacidade e segurança

As leituras de sensores são dados sensíveis que podem estar sujeitos a vários ataques de páginas da Web maliciosas. As implementações das APIs Generic Sensor impõem algumas limitações para reduzir os possíveis riscos de segurança e privacidade. Essas limitações precisam ser consideradas pelos desenvolvedores que pretendem usar a API. Por isso, vamos listá-las brevemente.

Somente HTTPS

Como a API Generic Sensor é um recurso avançado, o navegador só permite que ela seja usada em contextos seguros. Na prática, isso significa que, para usar a API Generic Sensor, você precisa acessar sua página por HTTPS. Durante o desenvolvimento, você pode fazer isso em http://localhost, mas para produção, é necessário ter HTTPS no servidor. Consulte a coleção Seguro e protegido para conferir práticas recomendadas e diretrizes.

Integração da Política de permissões

A integração da Política de permissões na API Generic Sensor controla o acesso aos dados de sensores de um frame.

Por padrão, os objetos Sensor só podem ser criados em um frame principal ou subframes de mesma origem, impedindo que iframes de origem cruzada façam leituras não autorizadas de dados de sensores. Esse comportamento padrão pode ser modificado ativando ou desativando explicitamente os recursos controlados por políticas correspondentes.

O snippet abaixo ilustra a concessão de acesso a dados do acelerômetro a um iframe de origem cruzada, o que significa que agora objetos Accelerometer ou LinearAccelerationSensor podem ser criados lá.

<iframe src="https://third-party.com" allow="accelerometer" />

A entrega de leituras do sensor pode ser suspensa

As leituras do sensor só podem ser acessadas por uma página da Web visível, ou seja, quando o usuário está interagindo com ela. Além disso, os dados do sensor não seriam fornecidos ao frame pai se o foco do usuário mudasse para um subframe de origem cruzada. Isso impede que o frame principal infira a entrada do usuário.

A seguir

Há um conjunto de classes de sensores já especificadas para serem implementadas em um futuro próximo, como Sensor de luz ambiente ou Sensor de proximidade. No entanto, graças à grande extensibilidade do framework Generic Sensor, podemos antecipar o surgimento de ainda mais novas classes que representam vários tipos de sensores.

Outra área importante para trabalhos futuros é a melhoria da própria API Generic Sensor. A especificação Generic Sensor é atualmente uma recomendação candidata, o que significa que ainda há tempo para fazer correções e trazer novas funcionalidades de que os desenvolvedores precisam.

Você pode ajudar!

As especificações do sensor atingiram o nível de maturidade Recomendação de candidato. Por isso, agradecemos muito o feedback dos desenvolvedores da Web e de navegadores. Informe quais recursos seriam interessantes adicionar ou se há algo que você gostaria de modificar na API atual.

Você também pode registrar problemas de especificação e bugs na implementação do Chrome.

Recursos

Agradecimentos

Este artigo foi revisado por Joe Medley e Kayce Basques.