giovedì 27 marzo 2014

Sviluppo e pubblicazione su Firefox OS

Dopo aver scritto la mia prima applicazione per Firefox Os, ho provato a pubblicarla sul Market di Firefox Os.

Di seguito viene descritto come sviluppare e pubblicare su questa piattaforma

Di fatto non esiste un SDK e tutte le operazioni di sviluppo si possono svolgere all'interno dell'emulatore di Firefox OS che si installa aprendo l'indirizzo

about:app-manager

nell'url di Firefox (versione 26 e successive)
Al primo avvio viene richiesto di installare l'emulatore e l'adb helper (per sviluppare direttamente sul telefono)


Nella finestra App si indica la posizione (directory) delle varie applicazioni in fase di sviluppo mentre nella finestra Dispositivo si puo' gestire il lancio e la chiusura dell'applicazione sia nell'emulatore che nel telefono fisico


In basso alla finestra Dispositivo si ha la modalita' di connessione (nel caso di utilizzo di telefono fisico per ottenere la connessione e' necessario confermare anche sul telefono)
Per ogni modifica si deve effettuare un Aggiorna sulla finestra App e poi lanciare l'applicazione da dispositivi

Una volta terminato il lavoro si crea un file .zip che contiene l'applicazione (compresa di immagini, javascript e file manifest secondo le specifiche di Firefox e si va sul Firefox MarketPlace (usando Firefox), ci si registra (utilizzando Mozilla Persona) e poi si sottomette l'applicazione come file .zip


A questo punto il sito effettua dei controlli  e si rieffettua la procedura fino a quando non ci sono errori
(Non e' richiesto nessun pagamento, a differenza di Google dove e' forfettario o Apple dove e' annuale)

Si risponde ad una serie di domande (alcune anche curiose del tipo se l'applicazione fa propaganda nazista???)

E si attende pazientemente che l'applicazione sia approvata (e' un procedimento piuttosto lento al confronto di Play Store)




mercoledì 26 marzo 2014

Android Wear

Dopo aver modificato il telefono per avere un dispositivo ad Android 4.4, ho provato Android Wear, la versione per i futuri Smart Watch di Google.

Al momento e' disponibile solo una developer preview (per cui bisogna essere registrati) all'interno dell'emulatore

Sul telefono si installa l'applicazione Android Wear Preview App mentre dall'Android SDK Manager si deve scaricare Android Wear ARM EABI v7a System Image.


e si crea una nuova macchina virtuale AVD basata sull'immagine di Android Wear

A questo punto si esegue l'emulatore, si connette il telefono via USB e si attiva l'applicazione sul telefono.
Al computer si digita quindi il comando

adb -d forward tcp:5601 tcp:5601

l'applicazione sul telefono mostra lo stato di connessione e l'icona sull'emulatore mostra lo stato di connessione.
Per generare quindi una notifica ho effettuato una telefonata al cellulare e sul display dello Smart  Watch e' comparsa la notizia

Di fatto il sistema reagisce sostanzialmente alle notifiche che gli  derivano dal telefono a cui e' accoppiato.
Non e' necessario che le notifiche siano di sistema. Si puo' creare una applicazione che generi notifiche e queste vengono instradate verso lo smartwatch


Per esempio il codice sottostante genera una piccola applicazione che lancia una notifica all'avvio (codice modificato dal post originale, da notare che il codice originale non era stato concepito per Android Wear a dimostrazione di come si possa importare semplicemente codice relativo ad altre API)
Per compilare il codice e' necessario inserire tra le librerie wereable-preview-support.jar tra le Referenced Libraries
-----------------------------------------------------------------------------
package com.wear.orologio;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;


public class MainActivity extends Activity {

NotificationManager notificationManager;
Notification myNotification;
private static final int MY_NOTIFICATION_ID=1;
private final String myBlog = "http://www.google.com/";



@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Context context = getApplicationContext();
   Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(myBlog));
   PendingIntent pendingIntent = PendingIntent.getActivity(
     MainActivity.this, 
     0, 
     myIntent, 
     Intent.FLAG_ACTIVITY_NEW_TASK);
     
   myNotification = new Notification.Builder(context)
         .setContentTitle("Notifica")
         .setContentText("Dal Luca Innocenti")
         .setTicker("Notifica")
         .setWhen(System.currentTimeMillis())
         .setContentIntent(pendingIntent)
         .setDefaults(Notification.DEFAULT_SOUND)
         .setAutoCancel(true)
         .setSmallIcon(R.drawable.ic_launcher)
         .build();
   
   notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
   notificationManager.notify(MY_NOTIFICATION_ID, myNotification);


    }

}

-----------------------------------------------------------------------------
Ma Android Wear sara' solo notifiche???
Non sembra in quanto sull'emulatore dello Smart Watch funziona tranquillamente Hello World per cui il dispositivo sembra avere una propria autonomia dal telefono








Cyanogenmod 11 KitKat su Nexus S

Volevo provare ad usare Android Wear ma e' necessario avere un telefono con almeno Android 4.3 (non e' contemplato l'uso di tablet come il Nexus 7 2012) per cui ho deciso di modificare il Nexus S (rimasto fermo ad Android 4.1) per installare la Cyanogenmod 11 Android KitKat 4.4



La procedura prevede di scarica l'immagine Cyanogenmod da questo link e si carica il file cm-11-20131122-UNOFFICIAL-crespo.zip sulla memoria del telefono

Per poter installare il file e' necessario utilizzare la CWM Recovery da questo link ed in particolare il file recovery-clockwork-6.0.4.3-crespo.img (ho provato con la recovery gia' installata ma non ha funzionato)

per installare la CWM si collega il telefono, lo si accende con la combinazione Power + Vol Su e da shell si digita

fastboot flash recovery recovery-clockwork-6.0.4.3-crespo.img

a questo punto si rispenge e si riaccende il telefono sempre con la combinazione Power + Vol Su e si seleziona l'opzione Recovery (Vol Su e Vol Giu'per spostarsi, Power per selezionare), si effettua il wipe dei dati e si installa da zip file cm-11-20131122-UNOFFICIAL-crespo.zip

Le applicazioni Gooogle si scaricano da questo link e si installano con procedura simile (install from zip file)



Alla fine il telefono si riavvia con Android 4.4. E' forse un filino piu' lento della versione 4.1 ma e' assolutamente usabile (unico appunto e' che la tastiera di Unlock del pin della Sim card e' schiacciata ed i tasti non sono cosi' comodi da selezionare)

martedì 25 marzo 2014

Fractal Explorer su Firefox Os

Ed ecco il programmino per esplorare Mandelbrot in versione Firefox OS
Rispetto alla versione HTML5 pura ci sono alcune differenze


Per prima cosa e' stato necessario aggiungere un pulsante di Reset perche' premendo il pulsante Home del telefono l'applicazione non viene chiusa ma solo nascosta ed al successivo riavvio si presenta con l'ultimo livello di zoom impostato

In esecuzione sull'emulatore


In esecuzione su Alcatel One Touch Fire


Inoltre poter passare la validazione del Market di Firefox e' stato necessario dividere la parte HTML dalla sezione Javascript

Da questo link di puo' scaricare il file zip dell'applicazione

manifest.webapp
------------------------------------------------------------------------
{
  "version": "0.5",
  "name": "MandExplorer",
  "description": "A simple Mandelbrot Explorer with zoom function",
  "launch_path": "/index.html",
  "icons": {
    "16": "/app-icons/icon-16.png",
"32": "/app-icons/icon-32.png",
    "48": "/app-icons/icon-48.png",
"60": "/app-icons/icon-60.png",
"90": "/app-icons/icon-90.png",
"120": "/app-icons/icon-120.png",
    "128": "/app-icons/icon-128.png",
"256": "/app-icons/icon-256.png"
  },
  "developer": {
    "name": "Luca Innocenti",
    "url": "http://debiaonoldcomputers.blogspot.com/"
  },
  "locales": {
    "en": {
      "description": "A simple Mandelbrot Explorer with zoom function",
      "developer": {
        "url": "http://debiaonoldcomputers.blogspot.com/"
      }
    },
    "it": {
      "description": "Un semplice visualizzatore con zoomabile dell'insieme di Mandelbrot",
      "developer": {
        "url": "http://debiaonoldcomputers.blogspot.com/"
      }
    }
  },
  "default_locale": "en"
}
------------------------------------------------------------------------


index.html
------------------------------------------------------------------------
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
    <body>
<script src="./js/calcola.js"></script>

        <canvas id="mandelbrot"  height="400" width="320">
        Il Browser non supporta HTML5.
        <!-- Utile per inserire informazioni di default -->
        </canvas><br>
<center><input type="button" id="reset" value="Reset"></center>


    </body>
</html>
------------------------------------------------------------------------

calcola.js
------------------------------------------------------------------------
//inizializza le variabili
//dimensioni dello schermo
var SCREEN_HEIGHT = 400;
var SCREEN_WIDTH = 320;
//finestra di visualizzazione in coordinate Mandelbrot
var a1 = -2.0;
var b1 = -1.2;
var a2 = 1.0;
var b2 = 1.2;
//numero di cicli
var it = 25;
var delta_x ;
var delta_y; 
// palette di 12 colori
var colori = ["#FFFFFF","#FF0000","#00FF00","#0000FF","#FF00FF","#FFFF00","#00FFFF","#FFaa00","#abcedf","#fedc0a","#ab16dd","#d00fba","#edabcc","#ddacff"];

//function punto(x1,y1,

function disegna(r_mi,i_mi,r_ma,i_ma,ite)
{
//effettua il calcolo dell'insieme date le dimensioni della finestra
var canvas = document.getElementById("mandelbrot");
if (canvas.getContext) {

 var context = canvas.getContext("2d");
 //rende il fondo nero
 context.fillStyle="rgb(0,0,0)";
 context.fillRect(0, 0, canvas.width, canvas.height);
var re_min = r_mi;
var im_min = i_mi;
var re_max = r_ma;
var im_max = i_ma;
var iterazioni = ite;
var r;
var a,b;
var x,y,x_new,y_new;
var test;
var k,j,i;
var re_factor = (re_max-re_min);
var im_factor = (im_max-im_min);
for (var i=0;i<SCREEN_HEIGHT;i++)
{
for (var j=0;j<SCREEN_WIDTH;j++)
 {
 a = re_min+(j*re_factor/SCREEN_WIDTH);
 b = im_min+(i*im_factor/SCREEN_HEIGHT);
 x = 0;
 y = 0;
 test = 0;
 for (var k=0;k<iterazioni;k++)
  {
  x_new = (x*x)-(y*y)+a;
  y_new = (2*x*y)+b;
  if (((x_new*x_new)+(y_new*y_new))>4)
{
// colora il punto
r = k%12;
context.beginPath();
context.fillRect(j-1,i-1,1,1);
context.fillStyle=colori[r];
context.stroke();
break;
}
  x = x_new;
  y = y_new;
  }
 }
}
}
}


function doMouseDown(event)
{
canvas_x = event.pageX;
canvas_y = event.pageY;
//calcola il punto in coordinate Mandelbrot
var p1 = (((a2-a1)/SCREEN_WIDTH)*canvas_x) + a1;
var p2 = (((b2-b1)/SCREEN_HEIGHT)*canvas_y) + b1;
//calcola la dimensione della finestra (riduzione di 1/4)
var delta_x = (a2-a1)/4;
var delta_y = delta_x*(SCREEN_WIDTH/SCREEN_WIDTH);
//aumenta il numero di iterazioni per ogni ciclo
it = it + 75;
//definisce la finestra intorno al punto cliccato
a1 = p1 - delta_y;
b1 = p2 - delta_x;
a2 = p1 + delta_y;
b2 = p2 + delta_x;
disegna(a1,b1,a2,b2,it);
}

function res(event)
{
a1 = -2.0;
b1 = -1.2;
a2 = 1.0;
b2 = 1.2;
it = 25;
disegna(a1,b1,a2,b2,it);
}

window.onload = function()
{
//riscala alla dimensione della finestra
var canvas = document.getElementById("mandelbrot");
var button = document.getElementById("reset");

//aggiunge l'evento click del mouse 
canvas.addEventListener("mousedown",doMouseDown,false);
button.addEventListener("click", res,false);
disegna(a1,b1,a2,b2,it);
}

------------------------------------------------------------------------

Fractal Explorer in HTML5

Per provare a creare una applicazione per Firefox OS ho modificato il programmino in HTML5 per generare l'insieme di Mandelbrot per renderlo interattivo
In pratica cliccando sullo schermo con il mouse viene effettuato uno zoom centrato sul punto cliccato diminuendo la finestra di visualizzazione di 1/4



Il programma in esecuzione



Rispetto alla versione precedente e' stata creata anche una semplice palette da 12 colori

-----------------------------------------------------------
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        </style>
    </head>
    <body>
      
        <canvas id="mandelbrot" height="400" width="800">
        Il Browser non supporta HTML5.
        <!-- Utile per inserire informazioni di default -->
        </canvas>

<script type="text/javascript">
//inizializza le variabili
//dimensioni dello schermo
        var SCREEN_HEIGHT = 0;
        var SCREEN_WIDTH = 0;
//finestra di visualizzazione in coordinate Mandelbrot
var a1 = -2.0;
var b1 = -1.2;
var a2 = 1.0;
var b2 = 1.2;
//numero di cicli
var it = 100;
var delta_x ;
var delta_y; 
// palette di 12 colori
var colori = ["#FFFFFF","#FF0000","#00FF00","#0000FF","#FF00FF","#FFFF00","#00FFFF","#FFaa00","#abcedf","#fedc0a","#ab16dd","#d00fba","#edabcc","#ddacff"];

//function punto(x1,y1,

function disegna(r_mi,i_mi,r_ma,i_ma,ite)
{
//effettua il calcolo dell'insieme date le dimensioni della finestra
var canvas = document.getElementById("mandelbrot");
                if (canvas.getContext) {

                  var context = canvas.getContext("2d");
 //rende il fondo nero
 context.fillStyle="rgb(0,0,0)";
 context.fillRect(0, 0, canvas.width, canvas.height);
                    var re_min = r_mi;
                    var im_min = i_mi;
                    var re_max = r_ma;
                    var im_max = i_ma;
                    var iterazioni = ite;
                    var r;
                    var a,b;
                    var x,y,x_new,y_new;
                    var test;
                    var k,j,i;
                    var re_factor = (re_max-re_min);
                    var im_factor = (im_max-im_min);
                    for (var i=0;i<SCREEN_HEIGHT;i++)
                     {
                     for (var j=0;j<SCREEN_WIDTH;j++)
                      {
                      a = re_min+(j*re_factor/SCREEN_WIDTH);
                      b = im_min+(i*im_factor/SCREEN_HEIGHT);
                      x = 0;
                      y = 0;
                      test = 0;
                      for (var k=0;k<iterazioni;k++)
                       {
                       x_new = (x*x)-(y*y)+a;
                       y_new = (2*x*y)+b;
                       if (((x_new*x_new)+(y_new*y_new))>4)
                        {
// colora il punto
                        r = k%12;
context.beginPath();
context.fillRect(j,i,1,1);
context.fillStyle=colori[r];
    context.stroke();
                        break;
                        }
                       x = x_new;
                       y = y_new;
                       }
                      }
                     }
                }
}


function doMouseDown(event)
{
canvas_x = event.pageX;
canvas_y = event.pageY;
//calcola il punto in coordinate Mandelbrot
var p1 = (((a2-a1)/SCREEN_WIDTH)*canvas_x) + a1;
var p2 = (((b2-b1)/SCREEN_HEIGHT)*canvas_y) + b1;
//calcola la dimensione della finestra (riduzione di 1/4)
var delta_x = (a2-a1)/4;
var delta_y = delta_x*(SCREEN_WIDTH/SCREEN_WIDTH);
//aumenta il numero di iterazioni per ogni ciclo
it = it + 75;
   //definisce la finestra intorno al punto cliccato
a1 = p1 - delta_y;
b1 = p2 - delta_x;
a2 = p1 + delta_y;
b2 = p2 + delta_x;
disegna(a1,b1,a2,b2,it);
}

       window.onload = function()
            {
//riscala alla dimensione della finestra
var canvas = document.getElementById("mandelbrot");
if (canvas.width  < window.innerWidth)
            {
                canvas.width  = window.innerWidth;
SCREEN_WIDTH =  canvas.width;
            }

            if (canvas.height < window.innerHeight)
            {
                canvas.height = window.innerHeight;
SCREEN_HEIGHT = canvas.height;
            }
//aggiunge l'evento click del mouse 
canvas.addEventListener("mousedown",doMouseDown,false);
disegna(a1,b1,a2,b2,it);
            }
        </script>
    </body>
</html>

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>

Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...