venerdì 21 marzo 2014

Interazione tra Kinect ed XWindow con PyOpenNi

Francamente non amo Processing (a meno che non si declini come IDE di Arduino) ed dopo un po' di ricerche ho trovato il modo di pilotare Kinect mediante Python con la libreria PyOpenNi

L'installazione e' sostanzialmente manuale dai sorgenti con la procedura

git clone https://github.com/jmendeth/PyOpenNI.git 

mkdir PyOpenNI-build 
cd PyOpenNI-build 
cmake ../PyOpenNI
(per soddisfare le dipendenze sudo apt-get install cmake build-essential git-core \ python-dev libboost-python-dev)

Al termine della compilazione non c'e' la possibilita' di fare make install in  quanto e' lasciato all'utente la fase finale. Per i miei scopi la cosa piu' semplice e' stata quella di spostare il file openni.so dalla PyOpenNI-build/lib nella cartella degli esempi


Un esempio veramente interessate e' dato da hand-tracker.py che, brevemente modificato, puo' essere utilizzato per creare un mouse virtuale per X (nel brevissimo esempio se la mano si trova tra 750 e 1000 mm dal Kinect viene agganciato il Kinect, se si trova a meno di 750 mm si fa clic sul punto del mouse....lo script e' ampiamente migliorabile)

------------------------------------------------
#! /usr/bin/python

from openni import *
import os

context = Context()
context.init()

depth_generator = DepthGenerator()
depth_generator.create(context)
depth_generator.set_resolution_preset(RES_VGA)
depth_generator.fps = 30

gesture_generator = GestureGenerator()
gesture_generator.create(context)
gesture_generator.add_gesture('Wave')

hands_generator = HandsGenerator()
hands_generator.create(context)

# Declare the callbacks
# gesture
def gesture_detected(src, gesture, id, end_point):
    print "Detected gesture:", gesture
    hands_generator.start_tracking(end_point)
# gesture_detected

def gesture_progress(src, gesture, point, progress): pass
# gesture_progress

def create(src, id, pos, time):
    print 'Create ', id, pos
# create

def update(src, id, pos, time):
    print 'Update ', id, pos
    x = pos[0]+300
    y = pos[1]+300

    if (x < 0):
x = 0
 
    if (y < 0):
y = 0
    if ((pos[2] >750) and (pos[2] < 1000)):
os.system("xdotool mousemove "+ str(x)+" "+str(y))

    if (pos[2] <750):
os.system("xdotool mousemove "+ str(x)+" "+str(y) + " click 1")

# update

def destroy(src, id, time):
    print 'Destroy ', id
# destroy

# Register the callbacks
gesture_generator.register_gesture_cb(gesture_detected, gesture_progress)
hands_generator.register_hand_cb(create, update, destroy)

# Start generating
context.start_generating_all()

print 'Make a Wave to start tracking...'

while True:
    context.wait_any_update_all()
# while






Realta' aumentata con Aruco JS e WebGL

Una piccola introduzione: le librerie di realta' aumentata non gestiscono la fase di creazione degli oggetti 3D per cui si deve avere come integrazione una libreria grafica 3D

Detto cio' viene mostrato un breve esempio di come gestire un marker per la realta' aumentata catturato da una WebCam e creare un oggetto 3D che interagisca con il marker, il tutto gestito all'interno di un browser (in questo caso Firefox...Chrome non ha funzionato ma credo che sia dovuto ai permessi della webcam)

La libreria di AR utilizzata e' stata ArucoJS, la versione javascript di una libreria disponibile anche in C

Per prima cosa si deve stampare il marker (attenzione, una volta stampato il marker non deve essere tagliato a filo con il bordo nero ma deve essere lasciata una striscia bianca all'esterno del quadrato nero, altrimenti il marker non viene riconosciuto)


successivamente ho leggermente modificato l'esempio debug-posit presente nella cartella samples per generare un cubo al di sopra del marker con i comandi WebGL. Le modifiche, veramente minime, sono indicate dal segno giallo nel sorgente

Da notare che quando e' in esecuzione la pagina su Firefox, il processore entra in funzione in modo pesante con accensione delle ventole (su MacBook) e la pagina diventa un po' scattosa

il progetto puo' essere scaricato da questo link
---------------------------------------------------------------------------
<html>

<head>
  <title>Augmented Reality</title>

  <script type="text/javascript" src="libs/Three.js"></script> 

  <script type="text/javascript" src="svd.js"></script> 
  <script type="text/javascript" src="posit1.js"></script> 
  <script type="text/javascript" src="cv.js"></script> 
  <script type="text/javascript" src="aruco.js"></script> 

  <script>
    var video, canvas, context, imageData, detector, posit;
    var renderer1, renderer2, renderer3;
    var scene1, scene2, scene3, scene4;
    var camera1, camera2, camera3, camera4;
    var plane1, plane2, model, texture;
    var step = 0.0;

    var modelSize = 35.0; //millimeters

    function onLoad(){
      video = document.getElementById("video");
      canvas = document.getElementById("canvas");
      context = canvas.getContext("2d");
    
      canvas.width = parseInt(canvas.style.width);
      canvas.height = parseInt(canvas.style.height);
      
      navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
      if (navigator.getUserMedia){
        init();
      }
    };
    
    function init(){
      navigator.getUserMedia({video:true}, 
        function (stream){
          if (window.webkitURL) {
            video.src = window.webkitURL.createObjectURL(stream);
          } else if (video.mozSrcObject !== undefined) {
            video.mozSrcObject = stream;
          } else {
            video.src = stream;
          }
        },
        function(error){
        }
      );
      
      detector = new AR.Detector();
      posit = new POS.Posit(modelSize, canvas.width);

      createRenderers();
      createScenes();

      requestAnimationFrame(tick);
    };

    function tick(){
      requestAnimationFrame(tick);
      
      if (video.readyState === video.HAVE_ENOUGH_DATA){
        snapshot();

        var markers = detector.detect(imageData);
        drawCorners(markers);
        updateScenes(markers);
        
        render();
      }
    };

    function snapshot(){
      context.drawImage(video, 0, 0, canvas.width, canvas.height);
      imageData = context.getImageData(0, 0, canvas.width, canvas.height);
    };
    
    function drawCorners(markers){
      var corners, corner, i, j;
    
      context.lineWidth = 3;

      for (i = 0; i < markers.length; ++ i){
        corners = markers[i].corners;
        
        context.strokeStyle = "red";
        context.beginPath();
        
        for (j = 0; j < corners.length; ++ j){
          corner = corners[j];
          context.moveTo(corner.x, corner.y);
          corner = corners[(j + 1) % corners.length];
          context.lineTo(corner.x, corner.y);
        }

        context.stroke();
        context.closePath();
        
        context.strokeStyle = "green";
        context.strokeRect(corners[0].x - 2, corners[0].y - 2, 4, 4);
      }
    };

    function createRenderers(){
      renderer1 = new THREE.WebGLRenderer();
      renderer1.setClearColorHex(0xffff00, 1);
      renderer1.setSize(canvas.width, canvas.height);
      document.getElementById("container1").appendChild(renderer1.domElement);
      scene1 = new THREE.Scene();
      camera1 = new THREE.PerspectiveCamera(40, canvas.width / canvas.height, 1, 1000);
      scene1.add(camera1);

      renderer2 = new THREE.WebGLRenderer();
      renderer2.setClearColorHex(0xffff00, 1);
      renderer2.setSize(canvas.width, canvas.height);
      document.getElementById("container2").appendChild(renderer2.domElement);
      scene2 = new THREE.Scene();
      camera2 = new THREE.PerspectiveCamera(40, canvas.width / canvas.height, 1, 1000);
      scene2.add(camera2);

      renderer3 = new THREE.WebGLRenderer();
      renderer3.setClearColorHex(0xffffff, 1);
      renderer3.setSize(canvas.width, canvas.height);
      document.getElementById("container").appendChild(renderer3.domElement);
      
      scene3 = new THREE.Scene();
      camera3 = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5);
      scene3.add(camera3);
      
      scene4 = new THREE.Scene();
      camera4 = new THREE.PerspectiveCamera(40, canvas.width / canvas.height, 1, 1000);
      scene4.add(camera4);
    };

    function render(){
      renderer1.clear();
      renderer1.render(scene1, camera1);
      
      renderer2.clear();
      renderer2.render(scene2, camera2);

      renderer3.autoClear = false;
      renderer3.clear();
      renderer3.render(scene3, camera3);
      renderer3.render(scene4, camera4);
    };

    function createScenes(){
      plane1 = createPlane();
      scene1.add(plane1);

      plane2 = createPlane();
      scene2.add(plane2);
      
      texture = createTexture();
      scene3.add(texture);
    
      model = createModel();
      scene4.add(model);
    };
    
    function createPlane(){
      var object = new THREE.Object3D(),
          geometry = new THREE.PlaneGeometry(1.0, 1.0, 0.0),
          material = new THREE.MeshNormalMaterial(),
          mesh = new THREE.Mesh(geometry, material);
      
      object.add(mesh);
      
      return object;
    };
    
    function createTexture(){
      var texture = new THREE.Texture(video),
          object = new THREE.Object3D(),
          geometry = new THREE.PlaneGeometry(1.0, 1.0, 0.0),
          material = new THREE.MeshBasicMaterial( {map: texture, depthTest: false, depthWrite: false} ),
          mesh = new THREE.Mesh(geometry, material);
      
      object.position.z = -1;
      
      object.add(mesh);
      
      return object;
    };
    
    function createModel(){
      var object = new THREE.Object3D(),
        
          cubo_geometry = new THREE.CubeGeometry(1,1,1),
          cubo_material = new THREE.MeshNormalMaterial(),
          mesh = new THREE.Mesh(cubo_geometry, cubo_material);
      
      object.add(mesh);
      
      return object;
    };

    function updateScenes(markers){
      var corners, corner, pose, i;
      
      if (markers.length > 0){
        corners = markers[0].corners;
        
        for (i = 0; i < corners.length; ++ i){
          corner = corners[i];
          
          corner.x = corner.x - (canvas.width / 2);
          corner.y = (canvas.height / 2) - corner.y;
        }
        
        pose = posit.pose(corners);
        
        updateObject(plane1, pose.bestRotation, pose.bestTranslation);
        updateObject(plane2, pose.alternativeRotation, pose.alternativeTranslation);
        updateObject(model, pose.bestRotation, pose.bestTranslation);

        updatePose("pose1", pose.bestError, pose.bestRotation, pose.bestTranslation);
        updatePose("pose2", pose.alternativeError, pose.alternativeRotation, pose.alternativeTranslation);
        
        step += 0.025;
        
        model.rotation.z -= step;
      }
      
      texture.children[0].material.map.needsUpdate = true;
    };
    
    function updateObject(object, rotation, translation){
      object.scale.x = modelSize;
      object.scale.y = modelSize;
      object.scale.z = modelSize;
      
      object.rotation.x = -Math.asin(-rotation[1][2]);
      object.rotation.y = -Math.atan2(rotation[0][2], rotation[2][2]);
      object.rotation.z = Math.atan2(rotation[1][0], rotation[1][1]);

      object.position.x = translation[0];
      object.position.y = translation[1];
      object.position.z = -translation[2];
    };
    
    function updatePose(id, error, rotation, translation){
      var yaw = -Math.atan2(rotation[0][2], rotation[2][2]);
      var pitch = -Math.asin(-rotation[1][2]);
      var roll = Math.atan2(rotation[1][0], rotation[1][1]);
      
      var d = document.getElementById(id);
      d.innerHTML = " error: " + error
                  + "<br/>"
                  + " x: " + (translation[0] | 0)
                  + " y: " + (translation[1] | 0)
                  + " z: " + (translation[2] | 0)
                  + "<br/>"
                  + " yaw: " + Math.round(-yaw * 180.0/Math.PI)
                  + " pitch: " + Math.round(-pitch * 180.0/Math.PI)
                  + " roll: " + Math.round(roll * 180.0/Math.PI);
    };

    window.onload = onLoad;
  </script>

</head>

<body style="text-align: center; font-family: monospace;">

  <video id="video" width=320 height=240 autoplay="true" style="display:none;"></video>
  
  <div style="margin: 10px;"><strong>-= Augmented Reality =-</strong></div>
  <div style="width: 100%;">
    <div style="width: 650px; margin-left:auto; margin-right:auto;">
      <canvas id="canvas" style="width: 320px; height: 240px; float: left; border: solid 1px black;"></canvas>
      <div id="container" style="width: 320px; height: 240px; float: left; border: solid 1px black; background: green;"></div>
      <div style="clear: both;"></div>
      <div style="float: left; border: solid 1px black;">
        <div id="container1" style="width: 320px; height: 240px; background: red;"></div>
        <div id="pose1"></div>
      </div>
      <div style="float: left; border: solid 1px black;">
        <div id="container2" style="width: 320px; height: 240px; background: blue;"></div>
        <div id="pose2"></div>
      </div>
    </div>
  </div>
  <div style="clear: both;"></div>
  <div style="margin: 15px;"><strong>Powered by <a href="http://code.google.com/p/js-aruco/">js-aruco</a> and <a href="https://github.com/mrdoob/three.js">Three.js</a></strong></div>

</body>
  
</html>

mercoledì 19 marzo 2014

Aggiornamento Alcatel One Touch Fire a Firefox OS 1.2

Visto che oramai Alcatel non ha mostrato l'intenzione di effettuare l'upgrade a Firefox OS 1.2 mi sono deciso a farlo da solo e devo ammettere che e' una procedura di gran lunga piu' semplice rispetto a qualsiasi aggiornamento di firmware per telefoni Android



La procedura e' stata eseguita su Linux perche' non avevo voglia di litigare con i driver di Windows
Le istruzioni sono state riprese da questo sito

Si scarica prima il pacchetto di Firefox OS per Alcatel One Touche Fire da questo sito (nel mio caso  ho preso il pacchetto v1.2-20140127 per avere l'ultima versione stabile

Poi si aggiunge al file /etc/udev/rules.d/99-android.rules la seguente riga


SUBSYSTEM==”usb”, ATTR{idVendor}==”18d1″, ATTR{idProduct}==”d00d”, MODE=”0666″, OWNER=”{luca}”(sostituire luca con il proprio nome utente)

Si modifica le impostazioni del telefono con
Impostazioni -> Info sul Dispositivo -> Altre Informazioni -> Sviluppatore abilita Debug remoto e Abilita console.

A questo punto si riavvia il telefono in boot mode (tasto volume - ed accensione) ed il telefono si arresta sulla schermata bianca con la scritta Alcatel



e si lancia il file ./flash.sh


L'operazione e' estremamente veloce ed indolore. Ed ecco il telefono senza la personalizzazione TIM


(per effettuare uno screenshot pulsate di accensione e tasto Home)


WebGL con ThreeJS


Sempre per un uso didattico, per visualizzare gli assi e i piani di simmetria dei minerali, stavo cercando un sistema semplice ed interattivo. Ero rimasto a VRML ma i tempi sono cambiati e la soluzione piu' semplice e' caduta su WebGL e la libreria ThreeJS


questo link c'e' una fonte inesauriibile di esempi ed idee. Io ho preso e modificato l'esempio HelloWorld per creare un cubo con indicato un asse di rotazione di ordine 4, un asse di rotazione di ordine 3 e 3 piani di simmetria.
Il modello e' navigabile (rotazione, spostamento) con il solo mouse ed e' compatibile sia con Chrome che con Firefox alle versioni piu' recenti

Il progetto completo e' scaricabile da qui

---------------------------------------------------------
<!doctype html>
<html lang="en">
<head>
<title>Solido</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <link rel=stylesheet href="css/base.css"/>
</head>
<body>

<script src="js/Three.js"></script>
<script src="js/Detector.js"></script>
<script src="js/Stats.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/THREEx.KeyboardState.js"></script>
<script src="js/THREEx.FullScreen.js"></script>
<script src="js/THREEx.WindowResize.js"></script>


<!-- ------------------------------------------------------------ -->

<div id="ThreeJS" style="z-index: 1; position: absolute; left:0px; top:0px"></div>
<script>

// standard global variables
var container, scene, camera, renderer, controls, stats;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();

var cube;

init();
animate();


function init() 
{
scene = new THREE.Scene();
var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
scene.add(camera);
camera.position.set(0,50,40);
camera.lookAt(scene.position);


if ( Detector.webgl )
renderer = new THREE.WebGLRenderer( {antialias:true} );
else
renderer = new THREE.CanvasRenderer(); 

renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);

// attach div element to variable to contain the renderer
container = document.getElementById( 'ThreeJS' );
// alternatively: to create the div at runtime, use:
//   container = document.createElement( 'div' );
//    document.body.appendChild( container );

// attach renderer to the container div
container.appendChild( renderer.domElement );

////////////
// EVENTS //
////////////

// automatically resize renderer
THREEx.WindowResize(renderer, camera);
THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });

//////////////
// CONTROLS //
//////////////

// move mouse and: left   click to rotate, 
//                 middle click to zoom, 
//                 right  click to pan
controls = new THREE.OrbitControls( camera, renderer.domElement );



//////////////
// GEOMETRY //
//////////////


var cubeMaterialArray = [];
// order to add materials: x+,x-,y+,y-,z+,z-
cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0xff3333 } ) );
cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0xff8800 } ) );
cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0xffff33 } ) );
cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0x33ff33 } ) );
cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0x3333ff } ) );
cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0x8833ff } ) );
var cubeMaterials = new THREE.MeshFaceMaterial( cubeMaterialArray );
// Cube parameters: width (x), height (y), depth (z), 
//        (optional) segments along x, segments along y, segments along z
var cubeGeometry = new THREE.CubeGeometry( 10, 10, 10, 0, 0, 0 );
// using THREE.MeshFaceMaterial() in the constructor below
//   causes the mesh to use the materials stored in the geometry
cube = new THREE.Mesh( cubeGeometry, cubeMaterials );
cube.position.set(0, 0, 0);
scene.add( cube );

material = new THREE.LineBasicMaterial({
        color: 0x0000ff
    });

var geometry = new THREE.Geometry();
    geometry.vertices.push(new THREE.Vector3(0, 0, 10));
    geometry.vertices.push(new THREE.Vector3(0, 0, -10));
var line = new THREE.Line(geometry, material);
scene.add(line);

var plane = new THREE.Mesh(new THREE.PlaneGeometry(30, 30), new THREE.MeshNormalMaterial({ color: 0x888888, transparent: true, opacity: 0.5  }));
plane.material.side = THREE.DoubleSide;
    plane.overdraw = true;
    scene.add(plane);

    
    var plane2 = new THREE.Mesh(new THREE.PlaneGeometry(1,1,5, 5), new THREE.MeshNormalMaterial({ color: 0xff0000, transparent: true, opacity: 0.5  }));
plane2.material.side = THREE.DoubleSide;
    plane2.overdraw = true;
    scene.add(plane2);


}

function animate() 
{
    requestAnimationFrame( animate );
render();
update();
}

function update()
{
// delta = change in time since last call (in seconds)
var delta = clock.getDelta(); 

controls.update();
stats.update();
}

function render() 
{
renderer.render( scene, camera );
}

</script>

</body>
</html>

martedì 18 marzo 2014

Comandare Impress con Raspberry + HC-SR04 + Python

Sulla base del precedente post  mi sono chiesto se era possibile ridurre al minimo la spesa (ed il consumo di corrente). In questa ottica ho provato a montare una Raspberry che si interfaccia direttamente con l'HC-SR04 per fare una presentazione. In questo caso si ha la comodita' di poter far girare la presentazione anche su televisori di generose dimensioni vista l'uscita HDMI (nel test e' stato usato un mini monitor ma solo perche' il televisore di casa era occupato)



Il collegamento tra le porte GPIO e la Raspberry e' il seguente
VCC 5 V
GND
GPIO 17 connesso a Trigger
GPIO 27 connesso a Echo con con una resistenza da 1 KOhm


Dato che la prova e' stata fatta con PiBang e non Raspbian c'e' da montare la libreria Gpio per Python che non e' inclusa di default
Per fare cio'
apt-get install build-essential python-dev python-pip
pip install rpi.gpio

il comando xdotool invece e' gia' compreso in PiBanf
lo script che ho utilizzato deriva da questo sito leggermente modificato
Quando la distanza risulta inferiore a 30 cm viene cambiata la diapositiva 
------------------------------------------------------------

#!/usr/bin/python
import os

contatore = 1

def reading(sensor):
    import time
    import RPi.GPIO as GPIO
    
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    
    if sensor == 0:
        GPIO.setup(17,GPIO.OUT)
        GPIO.setup(27,GPIO.IN)
        GPIO.output(17, GPIO.LOW)
        time.sleep(0.3)

        GPIO.output(17, True)
        time.sleep(0.00001)
        GPIO.output(17, False)

        while GPIO.input(27) == 0:
          signaloff = time.time()

        while GPIO.input(27) == 1:
          signalon = time.time()
        
        timepassed = signalon - signaloff
        distance = timepassed * 17000
        return distance
        
        GPIO.cleanup()

    else:
        print "Incorrect usonic() function varible."

while True:        
if (reading(0) < 30):
contatore = contatore + 1
print contatore
if (contatore >= 4):
contatore = 1
os.system("xdotool key 1")
os.system("xdotool key Return")
else:
os.system("xdotool key Right")
------------------------------------------------------------
Rispetto al precedente script si vede che i comandi di xdotool sono stati modificati. Non ho chiaro il motivo per cui e' stata necessaria la modifica ma penso che sia attribuibile al WM che e' differente tra Ubuntu (Gnome) e PiBang(OpenBox)

Con questa configurazione il costo dell'hardware (monitor o televisore escluso) e' dell'ordine dei 50 euro


lunedì 17 marzo 2014

Could not write bytes: Broken pipe su Ubuntu 12.04

L'errore "Could not write bytes: Broken pipe" e' comparso in modo improvviso e francamente non ho capito il motivo. Fatto e' che in fase di boot e' comparso questo messaggio e non era piu' possibile fare niente

Leggendo un po' sui forum di Ubuntu si dice di reinstallare tutto Xserver-Xorg.

La cosa piu' semplice e' partire in modalita' di recupero e selezione "Enable Network" (se si seleziona root si ha il filesystem montato in sola lettura e nessun supporto di rete)



a questo punto ho lanciato
apt-get install xserver-xorg xserver-xorg-video-all

ma niente da fare. allora da shell ho lanciato startx ed il comando non e' stato riconosciuto
Ho quindi installato anche il pacchetto xinit

apt-get install xinit

ed il sistema e' ripartito in modo corretto

venerdì 14 marzo 2014

Comandare Impress con Arduino + HC-SR04 + Python

Sempre alla ricerca di un sistema semplice e poco costoso per fare una postazione interattiva al Museo di Mineralogia, ho provato la strada Arduino

I difetti dei tentativi precedenti erano che Kinect costa un troppo mentre l'accoppiata Raspberry+Webcam e' troppo soggetta ad errori
In questo caso ho provato ad usare una Arduino accoppiata al sensore di distanza HC-SR04 (il costo finale e' inferiore ai 20 euro)


Nella prova ho usato una Arduino Due (nel caso reale va bene una Arduino Mini) che ho scoperto non avere la compatibilita' con la libreria NewPing. Per questo motivo e' stato usato lo sketch di esempio sul sito Arduino (rimuovendo la sezione relativa ai due led)
le connessioni sono semplici

Vcc
GND
Pin 13 : Echo
Pin 12 : Trigger

lo sketch manda la distanza via seriale ad un script in Python in ascolto. Se la distanza e' inferiore ai 50 cm viene cambiata la diapositiva

Video della prova


Con questa configurazione sono minimizzati i falsi click. In versione definitiva puo' essere utile mettere due sensori ultrasonici per permettere di andare avanti ed indietro nella presentazione

Programma Python
---------------------------------------
import serial,os, time

diapositiva = 1
ser = serial.Serial('/dev/ttyACM0', 9600)
while True:
distanza = int(ser.readline())

if (distanza < 50):
diapositiva = diapositiva + 1
if (diapositiva == 5):
os.system("xdotool key --window $(xdotool search --name 'presentazione.odp - LibreOffice Impress') 1")
os.system("xdotool key --window $(xdotool search --name 'presentazione.odp - LibreOffice Impress') KP_Enter")
diapositiva = 1
else:
os.system("xdotool key --window $(xdotool search --name 'presentazione.odp - LibreOffice Impress') Right")
----------------------------------------

Sketch Arduino
----------------------------------------
/*
 HC-SR04 Ping distance sensor]
 VCC to arduino 5v GND to arduino GND
 Echo to Arduino pin 13 Trig to Arduino pin 12
 Red POS to Arduino pin 11
 Green POS to Arduino pin 10
 560 ohm resistor to both LED NEG and GRD power rail
 More info at: http://goo.gl/kJ8Gl
 Original code improvements to the Ping sketch sourced from Trollmaker.com
 Some code and wiring inspired by http://en.wikiversity.org/wiki/User:Dstaub/robotcar
 */

#define trigPin 13
#define echoPin 12

void setup() {
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

void loop() {
  long duration, distance;
  digitalWrite(trigPin, LOW);  // Added this line
  delayMicroseconds(2); // Added this line
  digitalWrite(trigPin, HIGH);
//  delayMicroseconds(1000); - Removed this line
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = (duration/2) / 29.1;
  if (distance < 4) {  // This is where the LED On/Off happens
}
  else {
  }
  if (distance >= 200 || distance <= 0){
    Serial.println(200);
  }
  else {
    Serial.println(distance);
  }
  delay(500);
}


Update Plotly Dash Csv

    from dash import Dash , html , dcc , callback , Output , Input , State import plotly . express as px import pandas as pd import...