Usando dados do acelerômetro de um smartphone com o browser

Enquanto migrava meus nós de uma Rede de Sensores sem Fio baseada em 802.15.4 para “Internet of Things” baseado em 802.11, principalmente em aplicações voltadas a robótica e movimento, onde os dados do acelerometro eram requeridos, precisei constantemente usar o Android Studio ou o APPinventor do MIT, e isso é algo contraproducente. Procurando alternativas mais diretas, acabei conhecendo o evento ‘devicemotion’ do javascript, e isso tornou as coisas muito mais divertidas!
Saber se o evento é suportado(tentei em muitos devices e é em todos) é bem fácil:

if (window.DeviceMotionEvent) {
  document.getElementById("INFO").innerHTML = "Acelerometro suportado"
} else {
  document.getElementById("INFO").innerHTML = "Acelerometro não suportado"
}

E usá-lo é ainda mais fácil:

  window.addEventListener('devicemotion', dmHandler, false);

O evento é hookado e chama uma função, dmHandler no caso, para tratá-lo. Uma exemplo de um handler seria:

function dmHandler(eventData) {
  acceleration = eventData.accelerationIncludingGravity;
  var left = 0; var right = 0;
  if (Math.abs(acceleration.y) > 1) {
    var speed = acceleration.y * 123;
    left = Math.min(1023, speed + acceleration.x * 100);
    right = Math.min(1023, speed - acceleration.x * 100);
  } else if (Math.abs(acceleration.x) > 1) {
    var speed = Math.min(1023, Math.abs(acceleration.x) * 123);
    if (acceleration.x > 0) {
      left = speed; right = -speed; 
    } else {
      left = -speed; right = speed;
    }
  }
  var direcao = "";
  direcao = "[" + Math.round(acceleration.x) + "," + Math.round(acceleration.y) + "," + Math.round(acceleration.z) + "]<BR/>" + Math.round(left) + ", " + Math.round(right); 
  document.getElementById("MOSTRA").innerHTML = direcao;
}

Depois disso, so chamar uma outra função para transferir os dados para o dispositivo:

var _ultimo = 0;
function diz(_esquerda, _direita) {
  var now = Date.now();
  if (_ultimo + 200 < now) {
     _ultimo = now; 
     var _req = new XMLHttpRequest();
     _req.open('GET', '/go/' + Math.round(_esquerda) + "," + Math.round(_direita), true);
     _req.send(null);
  }
}

E colocando tudo junto:

<!DOCTYPE HTML>
<html><head>
</head><body>
<div id="INFO"></div>
<br/>
<div id="MOSTRA"></div>
<br/>
<script type='text/javascript'>
if (window.DeviceMotionEvent) {
  window.addEventListener('devicemotion', dmHandler, false);
  document.getElementById("INFO").innerHTML = "Acelerometro suportado"
} else {
  document.getElementById("INFO").innerHTML = "Acelerometro nao suportado"
}
var _ultimo = 0;
function diz(_esquerda, _direita) {
  var now = Date.now();
  if (_ultimo + 200 < now) {
     _ultimo = now; 
     var _req = new XMLHttpRequest();
     _req.open('GET', '/go/' + Math.round(_esquerda) + "," + Math.round(_direita), true);
     _req.send(null);
  }
}

function dmHandler(eventData) {
  acceleration = eventData.accelerationIncludingGravity;
  var left = 0; var right = 0;
  if (Math.abs(acceleration.y) > 1) {
    var speed = acceleration.y * 123;
    left = Math.min(1023, speed + acceleration.x * 100);
    right = Math.min(1023, speed - acceleration.x * 100);
  } else if (Math.abs(acceleration.x) > 1) {
    var speed = Math.min(1023, Math.abs(acceleration.x) * 123);
    if (acceleration.x > 0) {
      left = speed; right = -speed; 
    } else {
      left = -speed; right = speed;
    }
  }
  var direcao = "";
  direcao = "[" + Math.round(acceleration.x) + "," + Math.round(acceleration.y) + "," + Math.round(acceleration.z) + "]<BR/>" + Math.round(left) + ", " + Math.round(right); 
  document.getElementById("MOSTRA").innerHTML = direcao;
}
</script>
</body></html>

E no meu embarcado, estou usando o bottle( https://paoloo.wordpress.com/2015/11/19/bottle-py-a-solucao-de-um-problema-que-nao-deveria-existir/ ) para gerenciar:

# -*- coding: utf-8 -*-
from bottle import request, route, run, static_file

@route('/')
def get_BASEdata():
    return static_file("loader.html", root="./")

@route('/go/<d>')
def get_SENTdata(d):
    print d # ou faz algo mais util, como enviar os valores para o controlador dos motores
    return d

run(host='0.0.0.0', port=8080, debug=True)

E é isso. Simples, direto e não requer nada além de um browser!

Bottle.py – a solução de um problema que nao deveria existir

Bottle( http://bottlepy.org/docs/dev/index.html ) é um micro web framwork em python leve e muito simples. O mais louco é que ele é distribuido em apenas um arquivo e não tem nenhuma dependencia, alem do interpretador python, o que facilita absurdamente o deploy em qualquer ambiente!!!
Ok, e dai?
Bem, vi muita gente fazendo deploy no raspberryPi de aplicações com front end web, usando apache, php, mysql, postgres, redis, ou até mesmo obscenidades como o tomcat. Por enquanto, isto não é punido com a morte, mas deve ser evitado a todo custo e se buscar leveza e simplicidade. Quando conheci o bottle, tive a certeza que isto que procurava!

Com suporte a templates, roteamento simplificado, gerenciamento de dados e um servidor built-in, não fica faltando nada para sua aplicação subir.

para instalar (deixar o .py no diretorio ou instalar via pip, com # pip install bottle) e ter várias ideias de uso, é muito bom perder um tempinho na página oficial, mas uma aplicação que me deixou emocionado foi fazer uma pseudo-API do GPIO para web, simples assim:

cat > webgpio.py << _EOF_
from bottle import route
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)

@route('/le/<pino:int>')
def readgpio(pino):
    GPIO.setup(pino, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
    saida = GPIO.input(pino)
    return '<pinos><%d>%s</%d></pinos>' % (pino,str(saida),pino)

@route('/escreve/<pino:int>/<val:int>')
def writegpio(pino,val):
    GPIO.setup(pino, GPIO.OUT)
    if val==0 or val ==1:
        GPIO.output(pino, (GPIO.LOW , GPIO.HIGH)[val])
        return 'escrito %d no pino %d' % (val, pino)
    else:
        return 'valores validos para escrita: 0 e 1'
_EOF_

Rodando…

$ python webgpio.py 
Bottle v0.13-dev server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

127.0.0.1 - - [19/Nov/2015 02:20:48] "GET /le/13 HTTP/1.1" 200 26
127.0.0.1 - - [19/Nov/2015 02:21:04] "GET /escreve/15/0 HTTP/1.1" 200 20
127.0.0.1 - - [19/Nov/2015 02:21:14] "GET /escreve/15/1 HTTP/1.1" 200 20

E fazendo os requests…

$ curl http://localhost:8080/le/13
<pinos><13>12.5</13></pinos>
$ curl http://localhost:8080/escreve/15/1
escrito 1 no pino 15
$ curl http://localhost:8080/escreve/15/2
valores validos para escrita: 0 e 1

Obviamente este código não é seguro nem otimizado, é apena um PoC. Mas ainda assim, este treco é lindo, leve e tem suporte a muita coisa. Se a interface de template dele não agradar, ele é facilmente integravel com o jinja2( http://jinja.pocoo.org/docs/dev/ ) e, principalmente para linux embarcado é o framework web definitivo(ok, isso é minha opinião pessoal, mas é o que realmente parece haha).

Lua + C + Python – Mais que uma combinação macabra, uma gambiarra sinistra

Antes de mais nada: Por que?
Eu precisava fazer deploy de uma shared library em um ARM com linux(um raspberry Pi) extremamente capado, e não podia instalar todas as tools de dev, pois estava no limite de espaço do cartão, devido a aplicação dele. Mas precisava desta shared lib para carregar no python, via ctypes, pois o mesmo usaria as informações extraidas do hardware pelo .c, interpretaria e daria ordens ao resto da eletrônica. Acontece que os parametros do C variavam muito, precisavam ser tunados constantemente. Foi daí que rolou a ideia do LUA.
A ideia foi boa? Bom, funcionou. E até que faz um pouco de sentido, de alguma forma haha

vamos la, o loader, em python, é simplezão:

$ cat > loader.py << _EOF_
import ctypes
biblioteca=ctypes.CDLL('./zoalib.so')
biblioteca.zoeira.restype = ctypes.c_int
biblioteca.zoeira.argtypes = [ctypes.c_int]
biblioteca.zoeira(5)
_EOF_

O loader em C:

$ cat >zoalib.c << _EOF_
#include "lua.h"
#include "lauxlib.h"

int zoeira(int a)
{
    double z;
    lua_State *L = lua_open();
    luaL_openlibs(L);
    luaL_loadfile(L, "zoeira.lua");	/* arquivo lua */
    lua_pcall(L, 0, 0, 0);		/* limpa a pilha */
    lua_getglobal(L, "modelamotor");	/* nome da função do lua */
    lua_pushnumber(L, 3);		/* envia primeiro argumento */
    lua_pushnumber(L, 4);		/* envia segundo argumento */
    lua_pcall(L, 2, 1, 0);
    z = lua_tonumber(L, -1);		/* pega resultados */
    printf("Resultado: %f\n",z);
    lua_pop(L, 1);
    lua_close(L);
    return (a+1);
}
_EOF_

E o script em lua:

$ cat > zoeira.lua << _EOF_
function modelamotor (param1, param2)
    return math.sqrt(param1^2 + param^2)
end
_EOF_

Agora, vamos compilar com estes poucos parametros e rodar haha:

$ gcc -I/usr/include/lua5.1 zoalib.c -llua5.1 -lm -fPIC -shared -Wl,-soname,zoalib -o zoalib.so
$ python l.py
Resultado: 5.000000
6

Hell yeah!
Embora isso seja absurdamente mais util nos raspberryPi/beaglebone, talvez haja algum uso no desktop, vai saber haha.

nodeMCU: Mais que uma dev Board, um vício

NodeMCU( http://nodemcu.com/index_en.html ) é uma plataforma de desenvolvimento baseada no SoC ESP8266, com stack TCP/IP, eLua e vários extensões do lua embarcadas, voltadas especialmente para aplicações de IoT.
O SoC ESP8266 usa uma CPU RISC de 32bits Tensilica Xtensa LX106, rodando a 80MH. Tem uma RAM de instruções de 64 Kb, uma RAM de dados de 96Kb e uma flash interna de 4 fucking MBs! A estrela do bolo é o IEEE 802.11 b/g/n com suporte a WEP,WPA/WPA2 disponível, além dos 16 pinos de GPIO, SPI, I2C, UART e ADC de até 10bits.

nodeMCU

No xubuntu, eu tive problemas de conectar nela com o minicom. Mas estou usando lindamento o screen:

$ screen /dev/ttyUSB0 9600

Lua/eLua é linda. coisas extremamente complexas são executadas com uma simplicidade absurda, como listar as redes 802.11 disponiveis:

> function lswifi()
>>  function listap(t)
>>    for k,v in pairs(t) do
>>      print(k.." : "..v)
>>    end
>>  end
>>  wifi.sta.getap(listap)
>>end
>lswifi()

listar os arquivos, o conhecido ‘ls’, é simples:

> for k,v in pairs(file.list()) do print(k,v) end

logar na rede wifi, então, piece of cake:

> wifi.setmode(wifi.STATION);
> wifi.sta.config("SSID","senha")

e pronto! so checar seu ip com:

print(wifi.sta.getip())

Aproveitei e fiz um wget rudimentar hahaha

conn=net.createConnection(net.TCP, false) 
conn:on("receive", function(conn, pl) file.open("oi.lua","a"); file.write(pl); file.close() end)
conn:connect(80,"121.41.33.127")
conn:send("GET / HTTP/1.1\r\nHost: www.nodemcu.com\r\n"
    .."Connection: keep-alive\r\nAccept: */*\r\n\r\n")

Escrevi alguns métodos do GNU coreutils, como ls, cp, mv, df… para facilitar a vida. Em breve posto no github e ponho o link aqui.

O legal é que existem uma infinidade de exemplos e aplicações para esta plaquinha. E ela ganhou um fã haha.

Enjoy!