Surfboard Data Feeder

Participantes:

Thaysa
Gunter

Resumo do projeto:

O projeto visa acoplar dispositivos eletrônicos numa prancha de surfe com intuito de coletar dados que contribuem com a experiência do surfista, processo fabril e com o meio ambiente.

Descrição do projeto:

O projeto visa acoplar dispositivos eletrônicos numa prancha de surf com intuito de coletar dados que contribuem com a experiência do surfista, processo fabril e com o meio ambiente.  Experiência com o surfista por disponibilizar informações sobre as características dele durante prática do surf, informações relevantes em tempo real sobre a área em que está,  entre outras. No processo fabril, pois com os dados da característica do surfista é possível construir pranchas com a distribuição da flutuação e flexibilidade adequada para um determinado surfista, de acordo com a maneira que surfa. Do meio ambiente, pois sensores acoplados na prancha podem coletar dados ambientais durante a prática do esporte para cientistas,  governo e  cidadãos.

Os dispositivos eletrônicos devem ser colocados em compartimentos desenvolvidos na  prancha. A fiação deve ser protegida por eletrodutos/conduítes (Espaguete termo-retrátil) compatíveis para serem instalados na prancha. Diversos sensores distribuídos na prancha podem coletar o traçado(trajeto da prancha no mar), flutuação, flexibilidade da prancha(deformação que a prancha sofre durante o surfe), posicionamento do surfista na prancha, a pressão entre o mar e a prancha(isso causa deformação na prancha, normalmente em manobras) e característica do mar da localidade.

O trajeto é possível coletar com o GPS, a flutuação pode ser calculada com o valor da pressão atmosférica, a flexibilidade pode ser coletada por meio de sensores flexíveis, o posicionamento do surfista e a pressão podem ser coletados com sensor resistível a força e as característica do mar podem ser coletadas por meio de sensores analíticos, entre outros.

As dimensões da DragonBoard 410c e seu poder de processamento permitem que a placa seja acoplada na prancha sem interferir significativamente no peso da prancha e possibilitar coleta e acesso das informações pela rede.

A lista de sensores, atuadores e dispositivos desejáveis estão listadas a seguir, no entanto, o escopo para o contest está limitado a alguns destes dispositivos eletrônicos:

Coleta de dados da prática do surfe: sensor flexível, sensor de pressão ou resistor sensível a força, GPS, sensores inerciais (acelerômetro, giroscópio, magnetômetro e pressão atmosférica).

Coleta de dados do ambiente: temperatura, sensor de nível de oxigênio, sensor de nível de salinidade, índice de raio ultravioleta.

Informações em tempo real: notificação sobre alertas, ondas

Outros dispositivos: câmera

Pontos críticos

Desenvolvimento de encaixe na prancha com sistema de vedação, o qual é necessário para evitar entrada de água do mar. Preservar a longarina, pois é ela que proporciona a resistência e flexibilidade da prancha, em alguns pontos será preciso analisar a possibilidade de uso de comunicação sem fio para evitar passagem de fio através da longarina.  Sensores, placas e outros dispositivos não podem afetar o peso da prancha, de maneira que interfira negativamente no desempenho do surfista durante a prática do esporte. Deve-se sempre realizar cálculos que permitam balancear a distribuição de peso na prancha ocasionada pela inclusão dos dispositivos eletrônicos. Peças rígidas podem causar fissura na prancha.  Falhas no projeto de alimentação de energia dos dispositivos e conexão com a rede podem limitar o potencial do produto. Não foram encontradas patentes sobre o assunto, mas existe a possibilite de se deparar com alguma patente que exija ajustes no projeto para evitar problemas com propriedade intelectual.

 

Insumos Necessários

Os insumos referentes a Hardware e Software estão listados nas seções de mesmo nome. Seguem os demais insumos necessários:

  • Prancha
  • Bloco de prancha
  • Resina Material para construir os compartimentos/plugs( nylon ou fibra de vidro)
  • Deck (anti-derrapante de borracha EVA)
  • Fios
  • Espaguete termo-retrátil
  • outros

Perspectiva futura

Esse tipo de solução pode ser utilizado em campeonatos de surfe para auxiliar juízes e fornecer dicas ao surfista em como melhorar seu desempenho, identificar surfistas na praia, botão de pânico entre outras ideias e necessidades que possam surgir durante o desenvolvimento do projeto.

 

Plano Idealizado de desenvolvimento

Video

https://youtu.be/0CsssugsUT4

Instructables

https://www.instructables.com/id/Surfboard-Data-Feeder/

Histórico do desenvolvimento:

Semana 1 [2017/05/05-2017/05/14]

  • pesquisa e compra de: diferentes tipos de sensores flexíveis e extensômetro(strain gauge) para iniciar os primeiros testes de pressão, flexibilidade e deformação; giroscópio, acelerômetro e magnetômetro para coletar dados da movimentação da prancha de surf; e outros materiais auxiliares.
  • testes de sensor flexível;
  • testes com material da prancha de surf.

 


Semana 2 [2017/05/15-2017/05/21]

  • Ainda no aguardo da liberação de material que foi comprado em uma loja dos Estados Unidos via comércio eletrônico. Nosso material está na Fiscalização Aduaneira desde 10 de maio de 2017;
  • DragonBoard 410c, Linker Mezzanine Card Starter Kit for 96board e fonte chegaram. Primeiros testes foram realizados com sucesso;
  • estudos de como acoplar os dispositivos na prancha de surf;
  • testes com materiais alternativos disponíveis no Brasil para construir Force-Sensitive Resistor (FSR). Buscamos por Velostat, mas não foi encontrado no Brasil. Buscamos por algum material semicondutor que varia a resistência quando pressionado;
  • os testes na DragonBoard 410c foram feitos por meio do adb. O arquivo init.qcom.post_boot.sh foi alterado para iniciar o android com todas as gpios exportadas;
  • criamos uma apk + uso do código disponível em https://github.com/IOT-410c/DragonBoard410c_GpioLibrary com intuito de manipular alguns sensores com as gpios da placa.

 


Semana 3 [2017/05/22-2017/05/28]

  • Continuidade dos testes com a Dragonboard 410 + Linker Mezzanine Card Starter Kit for 96board;

Semana 4 [2017/05/22-2017/06/04]

  • Bloco da prancha encomendado. Daqui duas semanas será iniciado os testes na prancha.
  • testes de i2c e spi com a DragonBoard 410c.  (Ainda existe pendência neste item, pois ainda não integramos todos os sensores que precisamos );
  • inicio da construção de uma matriz de FSR;

 


Semana 5 [2017/06/05-2017/06/11]

  • testes com UART, SPI e I2C. Por algum motivo, nenhum input está funcionando com a Mezzanine. UART funcionou somente com acesso direto ao J8, o mesmo para o I2C( Testes ok somente para o par 19 e 21 da J8. O par 15 e 17 apenas uma vez, depois parou);
  • comunicação entre Dragonboard 410c e Arduino por UART;
  • programa básico para leitura de informação de sensor inercial (giroscópio, acelerômetro);
  • protótipo funcional da matriz  FSR;
  • testes GPS;
  • criação de obj pelo blender;
  • testes com Processing;
  • criação de conta aws e primeiros testes na dragonboard com aws-iot  https://github.com/ArrowElectronics/aws-iot-device-sdk;
  • testes webcam;

 

O obj criado e exportado no Blender carregou corretamente no Processing , conforme pode ser observado no vídeo, mas deu problema quando foi importado via JavaScript para visualização na aplicação web. Dito isso, optamos pelo modelo exemplo feito por Adafruit.

 

 

 


Semana 6 [2017/06/12-2017/06/18]

  • integração
  • ajustes no hardware. Soldagem de um dos circuitos que estavam na protoboard

Semana 7 [2017/06/19-2017/06/25]

  • finalizar software e hardware
  • adicionar hardware na prancha
  • testar
  • documentar projeto
  • houve defasagem em relação ao plano de desenvolvimento

 

Hardware:

  1. Qualcomm DragonBoard 410c
  2. Linker mezzanine card starter kit for 96Boards
  3. Arduino Uno
  4. TC4051B
  5. Resistor Sensível a Força (Force Sensitive Resistor)
  6. Sensor Flexível (Flex Sensor)
  7. Sensor de Raio Ultravioleta UV UVM-30A
  8. Adafruit BNO055 Absolute Orientation
  9. Mini Spy Camera with Trigger for Photo or Video
  10. 10K Precision Epoxy Thermistor
  11. Resistores diversos

Matriz de Resistor Sensível a Força (Force Sensitive Resistor Matrix)


BNO055


Termistor (Thermistor),  Sensor Flexível (Flex Sensor) e Sensor de Raio Ultravioleta

Conexão dos sensores analógicos em A0(0x80), A1(0x90), A2(0xA0) e A4(0xB0) da Linker Mezzanine card starter kit for 96board


Mini Spy Cam

Conexão via USB

 

Software/Firmware:

  1. Sistema Operacional Linux Linaro Debian 17.04.1
  2. C
  3. Python
  4. Java
  5. Arduino
  6. libsoc
  7. mraa
  8. upm
  9. OSGI Enroute
  10. Motion
  11. Fritizing
  12. JavaScript , HTML5 e outros para suporte no Front End

Arquitetura Geral

Implementação atual

 

 


Backend

 

GPS

O pré requisito para o GPS funcionar na DragonBoard 410c é instalar e iniciar o gpsd. Após esse passo é necessário colocar a placa em um ambiente a céu aberto.

sudo apt-get install gpsd-clients gnss-gpsd
sudo systemctl start qdsp-start.service
sudo systemctl start gnss-gpsd.service
sudo systemctl start qmi-gps-proxy.service
sudo systemctl restart gpsd

Ao executar o gpsmon é possível verificar a leitura do GPS.

O script de inicitalização do Surfboard Data Feeder (startup.sh) contém as linhas para iniciar os serviços necessários para iniciar a leitura dos satélites pelo GPS.

Aplicação em Python faz leitura do GPS e envia para a Surfboard Data Feeder REST API. Os dados então são processados pela Surfboard Data Feeder Provider armazenando-os temporariamente na DragonBoard 410c.

O site catb no link: http://www.catb.org/gpsd/client-howto.html apresenta uma explicação de como construir um client par a leitura das informações gpsd e o http://www.gpsinformation.org/dale/nmea.htm contém informações sobre as mensagens dos satélites.

O script Python abaixo é responsável pela leitura do GPS e envio para Surfboard Data Feeder API, o código em Python foi adaptado de Dan Mandle(http://dan.mandle.me e http://www.danmandle.com/blog/getting-gpsd-to-work-with-python/)

#! /usr/bin/python
# Adaptado de Dan Mandle http://dan.mandle.me September 2012
# License: GPL 2.0
 
import requests
import os
from gps import *
from time import *
import time
import threading


urlGps = 'http://linaro-alip:8080/rest/gps'

 
gpsd = None #seting the global variable
 
os.system('clear') #clear the terminal (optional)
 
class GpsPoller(threading.Thread):
  def __init__(self):
    threading.Thread.__init__(self)
    global gpsd #bring it in scope
    gpsd = gps(mode=WATCH_ENABLE) #starting the stream of info
    self.current_value = None
    self.running = True #setting the thread running to true
 
  def run(self):
    global gpsd
    while gpsp.running:
      gpsd.next() #this will continue to loop and grab EACH set of gpsd info to clear the buffer
 
if __name__ == '__main__':
  gpsp = GpsPoller() # create the thread
  try:
    gpsp.start() # start it up
    while True:
      #It may take a second or two to get good data
      os.system('clear')
      # gpsd.fix.latitude, gpsd.fix.longitude, gpsd.utc, gpsd.fix.time, gpsd.fix.altitude, gpsd.fix.eps, gpsd.fix.epx, gpsd.fix.epv, gpsd.fix.ept, gpsd.fix.speed, gpsd.fix.climb, gpsd.fix.track, gpsd.fix.mode, gpsd.satellites
      
      try:
        dataGps = '{"input":"'+str(gpsd.fix.latitude)+';'+str(gpsd.fix.longitude) +'","output":""}' 
        responseGps = requests.post(urlGps,data=dataGps,headers={"Content-Type": "application/json"})
	
      except:
        print "Unexpected error:", sys.exc_info()[0]
 
      time.sleep(5) #set to whatever
 
  except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
    print "nKilling Thread..."
    gpsp.running = False
    gpsp.join() # wait for the thread to finish what it's doing
  print "Done.nExiting."

 

BNO055

A instalação do mraa e upm permitem que seja possível acessar sensores via java. Como existe a biblioteca do BNO055 em java, então utilizou-se dessa linguagem para a leitura deste sensor. Logo, este módulo é responsável pela leitura do sensor, armazenamento e somente a operação GET é exposta na na camada da Surfboard Data Feeder REST API.

Seguem os passos que permitiram codificar o módulo de orientação em java:

Instalar Java SDK

sudo apt-get install default-jdk

Adicionar JAVA_HOME=usr/lib/jvm/default-java em /etc/enviroment

Gerar libmraajava.so

git clone https://github.com/intel-iot-devkit/mraa.git
cd mraa
mkdir build; cd build
cmake -D BUILDSWIGNODE=OFF -D BUILDSWIGPYTHON=OFF -D BUILDSWIGJAVA=ON ..
make mraajava # takes 27 seconds
cd src/java
cp libmraajava.so /usr/lib/
cp mraa.jar ${JAVA_HOME}/jre/lib/

Gerar libmraajava.so

git clone https://github.com/intel-iot-devkit/mraa.git
cd mraa
mkdir build; cd build
cmake -D BUILDSWIGNODE=OFF -D BUILDSWIGPYTHON=OFF -D BUILDSWIGJAVA=ON ..
make mraajava 
cd src/java
cp libmraajava.so /usr/lib/
cp mraa.jar ${JAVA_HOME}/jre/lib/

Se aparecer algum erro, talvez resolva instalando

sudo apt-get install software-properties-common
sudo apt install dirmngr

Executar o mesmo procedimento acima, mas no repositório do upm. É necessário passar o argumento no cmake para habilitar o java (-D BUILDSWIGJAVA=ON)

sudo cmake -DBUILDSWIGNODE=OFF -D BUILDSWIGJAVA=ON BUILDEXAMPLES=ON .. sudo java -Djava.library.path=/home/linaro/upm/build/src/bno055/ -jar upm-test2.jar

A referência utilizada para rodar o mraa e o upm com java pode ser acessada em: https://software.intel.com/en-us/iss-iot-edition-java-running-java-applications-outside-iss-iot

Após importar as dependências do upm e mraa no projeto java, então basta instanciar a classe upm_bno055.BNO055 passando como argumento o I2C e o endereço no I2C.

Segue o comando para localizar o I2C em que o sensor está conectado e o endereço do sensor no barramento:

sudo i2cdetect -y -r 0
sudo i2cdetect -y -r 1
sudo i2cdetect -y -r 3

Neste caso o i2c é o 1 e o endereço do sensor é 0x28.

Sensores Analógicos

Os sensores analógicos estão divididos em dois tipos, os que são lidos diretamente pela Mezzanine e a FSR Matrix lida pelo Arduino, o qual se comunica por UART pela DragonBoard 410c.

Sensores analógicos linker Mezzanine:

O script sdf_a0.py executa o programa em C, o qual utiliza libsoc para acessar o SPI, então, após a leitura do output do arquivo C, o script faz uma requisição POST para Surfboard Data Feeder REST API. Segue o trecho do script em python:

 

import subprocess
import requests

urla0 = 'http://linaro-alip:8080/rest/ultraviolet'

process = subprocess.Popen(["./sdf_a0"],stdout=subprocess.PIPE, stderr=subprocess.PIPE)

for line in iter(process.stdout.readline,""):
data = line.strip()
response = requests.post(urla0,data=data,headers={"Content-Type": "application/json"})

FSR Matrix:

Código Arduino:

int pinOutS0 = 8;
int pinOutS1 = 9;
int pinOutS2 = 10;
int pinInMux = A0;
int muxState[8] = {0};
	
String msg;
void setup() {
  pinMode(pinOutS0,OUTPUT);
  pinMode(pinOutS1,OUTPUT);
  pinMode(pinOutS2,OUTPUT);
  Serial.begin(9600);
}
	
void loop() {
 updateMux();
 msg = "";
for(int i = 0; i < 8; i++){
  if (i == 7){
     msg = msg +
     muxState[i];
  } else {
     msg = msg + muxState[i] + ",";
   }
}
 Serial.println(msg);
 delay(100);
 
}
	
void updateMux(){
 for (int i = 0; i < 8; i++){
	
 digitalWrite(pinOutS0, HIGH && (i & B00000001)); 
	digitalWrite(pinOutS1, HIGH && (i & B00000010));
	digitalWrite(pinOutS2, HIGH && (i & B00000100));
	muxState[i] = analogRead(pinInMux);
 } 
	
}

Código DragonBoard 410c:

import time
import serial
import re
import requests

ser = serial.Serial('/dev/tty96B0',baudrate=9600,timeout=0.0)
urlFsr = 'http://linaro-alip:8080/rest/fsrmatrix'

while 1:
    dataReceived = ser.readline()
    #if (re.match(r'^d',dataReceived)): 
    responseFsr = requests.post(urlFsr,data=dataReceived.strip(),headers={"Content-Type": "application/json"})
    time.sleep(0.05)
ser.close()

Frontend

Mapa e GPS
Openstreemap, Leaflet e JQuery são utilizados para apresentar o mapa, traçar caminho percorrido, requisição de dados por ajax do GPS para a API e atualização da informação atualizada na página.

function requestGps() {

    $.ajax({
        url : "/rest/gps/",
    }).done(
            function(data) {
                var gpsData = JSON.parse(data);
                mymap.panTo(new L.LatLng(gpsData.latitude, gpsData.longitude));
                L.marker([ gpsData.latitude, gpsData.longitude ]).addTo(mymap)
                        .bindPopup("<b>Última posição</b>").openPopup();
                L.polyline(
                        [ [ latestLatitude, latestLongitude ],
                                [ gpsData.latitude, gpsData.longitude ] ])
                        .addTo(mymap);
                $('#latitude').text(gpsData.latitude);
                $('#longitude').text(gpsData.longitude);
                latestLatitude = gpsData.latitude;
                latestLongitude = gpsData.longitude;
            }).fail(function(data) {

    }).always(function() {

    });
}
window.setInterval(requestGps, 2000);

Vídeo Stream
Após a instalação do motion é possível acessar o servidor web habilitado que disponibiliza o stream, conforme código abaixo:

<iframe class="embed-responsive-item" src="http://linaro-alip:8081/"  width="300" height="300"> </iframe>

Pressão na Prancha
Com o uso do canvas do HTML5 é possível criar elementos gráficos. Abaixo está o código que atualiza a força aplicada sobre a prancha:

//FSR
var frsMatrix = [];
var rows = 4;
var cols = 2;

function drawFsr() {
    $.ajax({
        url : "/rest/fsrmatrix/",

    }).done(function(data) {
        
        frsMatrix = data;

    }).fail(function(data) {
        // TODO
    }).always(function() {
        // TODO
    });

    var context = fsrCanvas.getContext('2d');
    context.clearRect(0, 0, fsrCanvas.width, fsrCanvas.height);
    var stepRow = 0;
    var stepCol = 0;

    for (var r = 0; r < rows; r++) {
        stepRow += 50;
        for (var c = 0; c < cols; c++) {
            stepCol = c % 2 == 0 ? 100 : 170;

            context.beginPath();
            context.arc(stepCol, stepRow, 20, 0, 2 * Math.PI);
            context.strokeStyle = '#0080c0';
            context.stroke();
            context.fillStyle = '#000000';
            
            var alpha = frsMatrix[r + c] * 0.05;
            context.globalAlpha = alpha;
            context.fill();

        } 

    } 

}
window.setInterval(drawFsr, 500);

Flexibilidade
Com o uso do canvas do HTML5 é possível criar elementos gráficos. Abaixo está o código que atualiza o painel de flexibilidade da prancha:

// flexibility
function drawFlexibility(data) {

    var context = flexCanvas.getContext('2d');
    context.clearRect(0, 0, flexCanvas.width, flexCanvas.height);
    context.beginPath();
    context.moveTo(170, 0);
    //context.bezierCurveTo(170, 170, 170, 170, 170, 170);
    context.bezierCurveTo(170, data, data, data, 170, 170);
    context.lineWidth = 10;

    // line color
    context.strokeStyle = 'black';
    context.stroke();
    //http://www.html5canvastutorials.com/tutorials/html5-canvas-bezier-curves/
}
window.setInterval(drawFlexibility, 10000);

Orientação
Código adaptado de Adafruit

var sceneWidth = 300;
var sceneHeight = 260;
// Define list of 3D models.  Each item should have a name property that
// will be rendered in the drop down, and a load function that is called
// with the model instance and should add a model property with a Three.js
// scene graph object that will be rendered.
var models = [
        {
            name : 'Surfboard',
            load : function(model) {
                // Build some cylinders and rotate them to form a cross of the XYZ axes.
                model.model = new THREE.Group();
                var xAxis = new THREE.Mesh(new THREE.CylinderGeometry(0.25,
                        0.25, 7, 32, 32), material);
                xAxis.rotation.z = 90.0 * (Math.PI / 180.0);
                model.model.add(xAxis);
                var yAxis = new THREE.Mesh(new THREE.CylinderGeometry(0.25,
                        0.25, 7, 32, 32), material);
                model.model.add(yAxis);
                var zAxis = new THREE.Mesh(new THREE.CylinderGeometry(0.25,
                        0.25, 7, 32, 32), material);
                zAxis.rotation.x = 90.0 * (Math.PI / 180.0);
                model.model.add(zAxis);
            }
        },
        {
            name : 'XYZ Axes',
            load : function(model) {
                // Build some cylinders and rotate them to form a cross of the XYZ axes.
                model.model = new THREE.Group();
                var xAxis = new THREE.Mesh(new THREE.CylinderGeometry(0.25,
                        0.25, 7, 32, 32), material);
                xAxis.rotation.z = 90.0 * (Math.PI / 180.0);
                model.model.add(xAxis);
                var yAxis = new THREE.Mesh(new THREE.CylinderGeometry(0.25,
                        0.25, 7, 32, 32), material);
                model.model.add(yAxis);
                var zAxis = new THREE.Mesh(new THREE.CylinderGeometry(0.25,
                        0.25, 7, 32, 32), material);
                zAxis.rotation.x = 90.0 * (Math.PI / 180.0);
                model.model.add(zAxis);
            }
        } ];

// Global state.
var bnoData = null;
var offset = null;
var orientation = null;
var objMTLLoader = new THREE.OBJMTLLoader();
var stlLoader = new THREE.STLLoader();
var currentModel = null;

// Setup Three.js scene and camera.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, sceneWidth / sceneHeight, 0.1,
        1000);
// Start with the camera moved back a bit to look directly at the origin.
camera.position.z = 10;

// Setup Three.js WebGL renderer and add it to the page.
var renderer = new THREE.WebGLRenderer();
renderer.setSize(sceneWidth, sceneHeight);
renderer.setClearColor(0xff0000, 0);
$('#renderer').append(renderer.domElement);
$('#renderer canvas').addClass('center-block'); // Center the renderer.

// Create white material for the models.
//var material = new THREE.MeshPhongMaterial({ color: 0xffffff });
var material = new THREE.MeshPhongMaterial({
    color : 0x2194ce
});

// Setup 3 point lighting with a red and blue point light in upper left
// and right corners, plus a bit of backlight from the rear forward.
var pointLight1 = new THREE.PointLight(0xffbbbb, 0.6);
pointLight1.position.set(40, 15, 40);
scene.add(pointLight1);
var pointLight2 = new THREE.PointLight(0xbbbbff, 0.6);
pointLight2.position.set(-40, 15, 40);
scene.add(pointLight2);
var backLight = new THREE.DirectionalLight(0xffff, 0.3);
backLight.position.set(0, -0.25, -1);
scene.add(backLight);

// Create a couple groups to apply rotations to the 3D model at different
// stages.  The outer group called offset is set to the reverse rotation
// of the current BNO orientation when the 'Straighten' button is clicked.
// This will force the model to center itself staring directly out of
// the screen.  The inner group called orientation will be rotated with
// the current BNO sensor orientation and cause the model to rotate.
offset = new THREE.Group();
orientation = new THREE.Group();
offset.add(orientation);
scene.add(offset);

// Main rendering function.
function render() {
    requestAnimationFrame(render);
    // Switch to the first model once it's loaded.
    if (currentModel === null) {
        if (models[0].hasOwnProperty('model')) {
            currentModel = 0;
            orientation.add(models[0].model);
        }
    }
    // Update the orientation with the last BNO sensor reading quaternion.
    if (bnoData !== null) {
        orientation.quaternion.set(parseFloat(bnoData.quatX),
                parseFloat(bnoData.quatY), parseFloat(bnoData.quatZ),
                parseFloat(bnoData.quatW));
    }
    renderer.render(scene, camera);
}
render();

// Populate drop-down of 3D models and load all the models..
$.each(models, function(index, model) {
    model.load(model);
});

// Function called when a new sensor reading is received.
function updateSensorData(data) {

    // Save the reading then update the UI.
    bnoData = data;
    $('#heading').text(data.heading);
    $('#roll').text(data.roll);
    $('#pitch').text(data.pitch);
    $('#calSys').text(data.calSys);
    $('#calGyro').text(data.calGyro);
    $('#calAccel').text(data.calAccel);
    $('#calMag').text(data.calMag);
    $('#temp').text(data.temp);
    $('#quat')
            .text(
                    data.quatX + ";" + data.quatY + ";" + data.quatZ + ";"
                            + data.quatW);

}

function requestOrientation() {
    $.ajax({
        url : "/rest/orientation/",
    }).done(function(data) {
        updateSensorData(JSON.parse(data));
    }).fail(function(data) {
        // TODO
    }).always(function() {
        // TODO
    });
}
window.setInterval(requestOrientation, 1000);

Ambiental
Com uma requisisação GET para a Surfboard Data Feeder API para o sensor desejado (/rest/<sensorname>) é possível obter o último valor lido do sensor. Segue o código para recuperar informação sobre raio ultravioleta:

function requestUvIndex() {
    $.ajax({
        url : "/rest/ultraviolet/",
    }).done(function(data) {

        var jsonData = JSON.parse(data);
        $('#uv').text(jsonData.value);
        var uvValue = jsonData.value;
        var uvIndexColor = '';
        //https://en.wikipedia.org/wiki/Ultraviolet_index
        if (uvValue <= 2.9) {
            uvIndexColor = "green";
        } else if (uvValue <= 5.9) {
            uvIndexColor = "yellow";
        } else if (uvValue <= 7.9) {
            uvIndexColor = "orange";
        } else if (uvValue <= 10.9) {
            uvIndexColor = "red";
        } else {
            uvIndexColor = "violet";
        }

        $('#uv').css('background', uvIndexColor);

    }).fail(function(data) {
        // TODO
    }).always(function() {
        // TODO
    });
}
window.setInterval(requestUvIndex, 10000);

 

Referências:

SmartFin: O projeto SmartFin , apoiado pela fundação Surfrider, tem por objetivo coletar dados do oceano por meio de sensores acoplados nas quilhas da prancha. Estes dados permitem identificar o pH, salinidade, características das ondas, temperatura, localização do GPS, oxigênio, clorofila do oceano em determinada localidade.

Surfsens (Pukas + Tecnalia): O projeto em parceria com Pukas e Tecnalia instala sensores na prancha para coletar informação durante o surf, o projeto é descrito no link: http://pukassurf.com/surfboards/surfsense/ . O vídeo do projeto pode ser assistido em: https://www.youtube.com/watch?v=ef8gbWrOirc

Opifex: O Opifex é um projeto brasileiro que tem por finalidade coletar informação do surf por meio de um dispositivo acoplado na prancha.

Instrumented Surfboard (ISurf): Projeto de estudantes da universidade Virginia Tech que coloca sensores na prancha de surf, o vídeo do projeto está disponível em: https://www.youtube.com/watch?v=uZaMBVInGSc

Samsung Galaxy Surfboard: A Samsung lançou no ano passado uma prancha de surf conceito nomeada de Galaxy Surfboard em parceria com Gabriel Medina. O link do vídeo da divulgação pode ser assistido em: https://www.youtube.com/watch?v=Y8yrfnT-PtI

Generate your perfect surfboard from data: Rob Renn, em sua tese em projeto industrial, projeta sistema de coleta e análise dos dados para projetar o formato da prancha ideal para o surfista. Isto está publicado de maneira resumida no instructables no link: http://www.instructables.com/id/GENERATE-YOUR-PERFECT-SURFBOARD-FROM-DATA

OnboardComputer:Projeto de estudantes da universidade de San Diego na California adiciona um computador de bordo em uma prancha de surf http://ucsdnews.ucsd.edu/archive/newsrel/science/08-11OnboardComputer.asp

Rastreador de movimentos: Existem diversos dispositivos que permitem rastrear os movimentos durante prática do esporte. Entre eles estão os Smartband/Smart Watches e outros dispositivos que são fixados na prancha como o Trace Up e Xensr Air 3D GPS

Fritizing DragonBoard: http://www.96boards.org/blog/fritzing-part-96boards-ce/  ou link direto http://rsalveti.net/pub/fritzing/96boards_CE.fzpz

BNO055: https://learn.adafruit.com/bno055-absolute-orientation-sensor-with-raspberry-pi-and-beaglebone-black/overview

GPS: http://www.catb.org/gpsd/client-howto.html