martedì 27 dicembre 2016

Firebase Database con Android

In questo post sono indicati i passi per interfacciare Android con i database di Firebase.
Il tutto e' stato ripreso dal tutorial ufficiale



Come per il caso delle notifiche si deve inserire nelle SDK tools Google Play Services e Repository



e si deve aggiungere il file google.json nella directory /app

build.gradle (Project)
--------------------
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'        
        classpath 'com.google.gms:google-services:3.0.0'
        // NOTE: Do not place your application dependencies here; they belong        // in the individual module build.gradle files    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
--------------------

build.gradle (app)
--------------------
apply plugin: 'com.android.application'
android {
    compileSdkVersion 25    buildToolsVersion "23.0.1"    
    defaultConfig {
    applicationId "com.luca_innocenti.dbfirebase"        
    minSdkVersion 19        
    targetSdkVersion 25        
    versionCode 1        
    versionName "1.0"        
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"    }
    buildTypes {
        release {
            minifyEnabled false            
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }
    }
}

dependencies {
    compile  'com.google.firebase:firebase-core:10.0.0'    
    compile  'com.google.firebase:firebase-database:10.0.0'
    compile  'com.google.firebase:firebase-auth:10.0.0'

    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'    })
    compile 'com.android.support:appcompat-v7:25.1.0'    
   compile 'com.android.support:design:25.1.0'    testCompile 'junit:junit:4.12'}

apply plugin: 'com.google.gms.google-services'
--------------------

Si parte creando una basic activity che abbia il nome corrispondente con quella selezionata nel DB




Nella activity sono implementata una lettura ed un update di un campo del DB

per effettuare l'update si devono modificare i permessi del DB. Dato che era una prova ci sono permessi di lettura e scrittura senza limitazioni (Firebase avvisa in modo vistoso che e' una scelta da non fare)



Activity
------------------------------
package com.luca_innocenti.dbfirebase;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import org.w3c.dom.Text;

public class MainActivity extends AppCompatActivity {

    TextView mConditionTextView;
    Button mButton;
    Button mLogin;
    Button mInsert;

    DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference();
    DatabaseReference mConditionRef = mRootRef.child("condition");

    private FirebaseAuth mAuth;
    private FirebaseAuth.AuthStateListener mAuthListener;
    private static final String TAG = "EmailPassword";



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

        mConditionTextView = (TextView) findViewById(R.id.textView2);
        mButton = (Button) findViewById(R.id.button2);
        mInsert = (Button) findViewById(R.id.button5);
        mLogin = (Button) findViewById(R.id.button4);

        // [START initialize_auth]        mAuth = FirebaseAuth.getInstance();
        // [END initialize_auth]
        // [START auth_state_listener]        mAuthListener = new FirebaseAuth.AuthStateListener() {
            @Override            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                FirebaseUser user = firebaseAuth.getCurrentUser();
                if (user != null) {
                    // User is signed in                    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
                } else {
                    // User is signed out                    Log.d(TAG, "onAuthStateChanged:signed_out");
                }
                // [START_EXCLUDE]                // [END_EXCLUDE]            }
        };
        // [END auth_state_listener]
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        fab.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }


    @Override    protected void onStart()
    {
        super.onStart();
        mAuth.addAuthStateListener(mAuthListener);

//qui viene aggiunto il listener, ogni volta che viene modificato il campo di interesse
// il valore modificato viene letto e mostrato nella textview 
        mConditionRef.addValueEventListener(new ValueEventListener() {
            @Override            public void onDataChange(DataSnapshot dataSnapshot) {
                String text = dataSnapshot.getValue(String.class);
                mConditionTextView.setText(text);
            }

            @Override            public void onCancelled(DatabaseError databaseError) {

            }
        });
// nel caso che si prema un
        mButton.setOnClickListener(new View.OnClickListener(){
            @Override            public void onClick(View view) {
              mConditionRef.setValue("Update");
            }

        });

        mInsert.setOnClickListener(new View.OnClickListener(){
            @Override            public void onClick(View view) {
                mConditionRef.setValue("Update");
            }

        });

        mLogin.setOnClickListener(new View.OnClickListener(){
            @Override            public void onClick(View view) {
                signIn("lucainnoc@gmail.com","chiara");
            }

        });


    }

    @Override    public void onStop() {
        super.onStop();
        if (mAuthListener != null) {
            mAuth.removeAuthStateListener(mAuthListener);
        }
    }

    private void signIn(String email, String password) {
        Log.d(TAG, "signIn:" + email);


        // [START sign_in_with_email]        mAuth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "signInWithEmail:onComplete:" + task.isSuccessful());

                        // If sign in fails, display a message to the user. If sign in succeeds                        // the auth state listener will be notified and logic to handle the                        // signed in user can be handled in the listener.                        if (!task.isSuccessful()) {
                            Log.d(TAG, "signInWithEmail:failed", task.getException());
                            Toast.makeText(getApplicationContext(), "Fallito",Toast.LENGTH_SHORT).show();
                        }

                        // [START_EXCLUDE]                        if (!task.isSuccessful()) {
                            Log.d(TAG, "signInWithEmail:successo");
                            Toast.makeText(getApplicationContext(), "Riuscito",Toast.LENGTH_SHORT).show();

                        }
                        // [END_EXCLUDE]                    }
                });
        // [END sign_in_with_email]    }


    @Override    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();

        //noinspection SimplifiableIfStatement        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
------------------------------

E adesso la prova modificando i dati sulla consolle di Firebase ed osservando le modifiche sull'emulatore Android (caricato con una immagine con Play Services)






Autenticazione Firebase con Android

Esempio di autenticazione basata su email/password su Firebase usando un client Android (ripreso dal GitHub ufficiale)
Per prima cosa dalla Consolle di Firebase si deve abilitare l'autenticazione mail/password



e si aggiunge un utente




build.gradle (progetto)
--------------
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'        classpath 'com.google.gms:google-services:3.0.0'
        // NOTE: Do not place your application dependencies here; they belong        // in the individual module build.gradle files    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
--------------

build.gradle (app)
--------------
apply plugin: 'com.android.application'
android {
    compileSdkVersion 25    buildToolsVersion "23.0.1"    defaultConfig {
        applicationId "com.luca_innocenti.dbfirebase"        minSdkVersion 19        targetSdkVersion 25        versionCode 1        versionName "1.0"        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"    }
    buildTypes {
        release {
            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }
    }
}

dependencies {
    compile  'com.google.firebase:firebase-core:10.0.0'
    compile  'com.google.firebase:firebase-auth:10.0.0'

    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'    })
    compile 'com.android.support:appcompat-v7:25.1.0'    compile 'com.android.support:design:25.1.0'    testCompile 'junit:junit:4.12'}
apply plugin: 'com.google.gms.google-services'
--------------


--------------
package com.luca_innocenti.dbfirebase;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import org.w3c.dom.Text;
import java.util.HashMap;import java.util.Map;
public class MainActivity extends AppCompatActivity {

    TextView mConditionTextView;
    Button mButton;
    Button mLogin;
    Button mInsert;

    DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference();
    DatabaseReference mConditionRef = mRootRef.child("condition");
    private FirebaseAuth mAuth;
    private FirebaseAuth.AuthStateListener mAuthListener;
    private static final String TAG = "EmailPassword";


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

        mConditionTextView = (TextView) findViewById(R.id.textView2);
        mButton = (Button) findViewById(R.id.button2);
        mInsert = (Button) findViewById(R.id.button5);
        mLogin = (Button) findViewById(R.id.button4);

        // [START initialize_auth]       
       //  Controlla se all'avvio l'utente e' gia' autenticato
                         mAuth = FirebaseAuth.getInstance();
        // [END initialize_auth]
        // [START auth_state_listener]        
                mAuthListener = new FirebaseAuth.AuthStateListener() {
            @Override            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                    FirebaseUser user = firebaseAuth.getCurrentUser();
                            if (user != null) {
                                  // User is signed in                    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
                                  } else {
                                 // User is signed out                    Log.d(TAG, "onAuthStateChanged:signed_out");
                                 }
                      // [START_EXCLUDE]                // [END_EXCLUDE]            }
               };
        // [END auth_state_listener]


        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        fab.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }


    @Override    protected void onStart()
    {
        super.onStart();
        mAuth.addAuthStateListener(mAuthListener);


        mConditionRef.addValueEventListener(new ValueEventListener() {
            @Override            public void onDataChange(DataSnapshot dataSnapshot) {
                String text = dataSnapshot.getValue(String.class);
                mConditionTextView.setText(text);
            }

            @Override            public void onCancelled(DatabaseError databaseError) {

            }
        });
        mButton.setOnClickListener(new View.OnClickListener(){
            @Override            public void onClick(View view) {
              mConditionRef.setValue("Update");
            }

        });

        mInsert.setOnClickListener(new View.OnClickListener(){
            private DatabaseReference usersRef;

            public void onClick(View view) {
                usersRef = mRootRef.child("condition");

                Map<String,Object> nick = new HashMap<String, Object>();
                nick.put("nome","Luca");
                usersRef.setValue(nick);
                Log.d(TAG,"inserimento");

            }

        });
         

       //se si clicca il pulsante viene richiamata la procedura di autenticazione
      // per semplicita' username e password sono statici
        mLogin.setOnClickListener(new View.OnClickListener(){
            @Override            public void onClick(View view) {
                signIn("lucainnoc@gmail.com","xxxxxxxx");
            }

        });


    }

    @Override    public void onStop() {
        super.onStop();
        if (mAuthListener != null) {
            mAuth.removeAuthStateListener(mAuthListener);
        }
    }

//Funzione di autenticazione 
    private void signIn(String email, String password) {
        Log.d(TAG, "signIn:" + email);


        // [START sign_in_with_email]        mAuth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "signInWithEmail:onComplete:" + task.isSuccessful());

                        // If sign in fails, display a message to the user. If sign in succeeds                        
                       // the auth state listener will be notified and logic to handle the                      
                      // signed in user can be handled in the listener.                       
                       if (!task.isSuccessful()) {
                            Log.d(TAG, "signInWithEmail:failed", task.getException());
                            Toast.makeText(getApplicationContext(), "Fallito",Toast.LENGTH_SHORT).show();
                        }

                        // [START_EXCLUDE]                        if (!task.isSuccessful()) {
                            Log.d(TAG, "signInWithEmail:successo");
                            Toast.makeText(getApplicationContext(), "Riuscito",Toast.LENGTH_SHORT).show();

                        }
                        // [END_EXCLUDE]                    }
                });
        // [END sign_in_with_email]    }


    @Override    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will        
// automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();

        //noinspection SimplifiableIfStatement        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
-------------

Debian dual monitor su Virtual Box

Come usare Debian usando VirtualBox in modalita' dual monitor

Per prima cosa si deve creare una macchina virtuale con due monitor




Dopo di cio' si installano le Guest Additions secondo la classica trafile

apt-get install build-essential
apt-get install linux-headers-$(uname -r)
VBoxLinuxAdditions.run 
reboot

A questo punto con la macchina virtuale attiva si deve abilitare il secondo schermo virtuale dal menu Visualizza




ed alla fine si manda tutto a pieno schermo


Android File Transfer e Mac

Ultimamente collegando il telefono a Mac non riuscivo piu' a scaricare il file mediante Android File Transfer



La soluzione e' quanto mai banale, Tirando giu' la tendina si deve cliccare su USB per la ricarica e si evidenziano le altre opzioni. Cliccando su Trasferimento File si accede al contenuto del telefono. La piccola cosa fastidiosa e' che si deve ripetere questa operazione ogni volta che si inserisce il cavo USB

DVD su XBox Orginal

Sono venuto in possesso del primo modello della XBox...non che smaniassi dalla voglia di provarla ma visto che c'ero l'ho collegata al televisore ...e sono iniziati i problemi



Al primo avvio sono riuscito a giocare a GTA fino a quando durante il caricamento di un livello da disco ottico si e' presentato il seguente errore


Per quanto ne sapevo la XBox primo modello era un PC (un P3 700 con scheda NVidia) con un vestito diverso e con i soli blocchi software. Su internet girano molti video che spiegano come pulire il DVD ma in caso di necessita' ho in casa diverse vecchie unita' ottiche da PC per cui pensavo in ogni caso di poterla sostituire




Per aprire il case e' sufficiente svitare le 4 torx nascoste sotto i piedini e le 2 nascoste sotto gli adesivi


ecco come si presenta il dispositivo a cofano aperto...un bel cavo IDE


Per smontare DVD ed HD e' necessario rimuovere 3 torx bronzate abbastanza nascoste per togliere lo chassis in plastica (togliere le viti e' facile...rimetterle decisamente meno)


e qui la prima amara sopresa...il DVD non e' standard PC.Ha un cavo dati EIDE ma l'alimentazione e' proprietaria...nessuna possibilita' di usare compenentistica PC..si deve cercare di ripulire l'unita'


il DVD si apre con 4 viti a croce standard

ed e' costruito in modo differente dai modelli PC. Espone completamente le ottiche per cui e' facile pulire


Rimontare l'unita' DVD pero' non e' banalissimo. Si incastra male e dato che e' portante deve essere stretta bene

Alla fine di tutto questo lavoro, riaccendo ma continua a non leggere DVD..lavoro sprecato




venerdì 23 dicembre 2016

Primi passi con Android Things su Intel Edison

Doveva chiamarsi Brillo ma alla fine IoT di Google e' diventato Android Things.

Per iniziare ad usarlo si deve configurare un dispositivo, nel mio caso una Intel Edison

Premessa: questa fase e' stata eseguita su due computer differenti perche' sulla mia Linux Box Centos 7 avevo qualche problema ad installare Platform Flash Tool di Intel (ed ho quindi usato Mac) mentre su Mac mi veniva generato un errore non comprensibile al momento di utilizzare fastboot (ed ho quindi usato Linux)

Si parte flashando il firmware della Edison scaricando il software da qui e l'immagine da qui (non ci sono altre impostazioni da fare ..di default si trova Flash file FlashEdison.json e Configuration Non_OS)

Si preme il tasto FW (SW1U3 il secondo da sinistra sopra al processore nell'Arduino Kit di Edison)
e si collega il cavo al connettore J16 (il tasto SW1 deve essere posizionato con il cursore verso J16)



a questo punto si clicca browse e si punta al file .zip e si attende

al termine delle operazioni si ha una schermata di questo tipo ed usando il comando
fastboot devices
si vede il dispositivo in modalita' fast boot
A questo punto si scarica entra nella directory dove e' stato scompattato il file precedentemente scaricato (androidthings_edison_devpreview_1.zip)

Ho provato a continuare a seguire la guida  ma su Mac e' stato generato il seguente comando


su Linux invece tutto procede bene

$ fastboot \
    flash gpt partition-table.img \
    flash u-boot u-boot-edison.bin \
    flash boot_a boot.img \
    flash boot_b boot.img \
    flash system_a system.img \
    flash system_b system.img \
    flash userdata userdata.img \
    erase misc \
    set_active _a

$ fastboot \
    flash gapps_a gapps.img \
    flash gapps_b gapps.img

$ fastboot \
    flash oem_a oem.img \
    flash oem_b oem.img

si riavvia quindi il dispositivo per togliere la modalita' fastboot e passare in modalita' device
fastboot reboot
adb devices
(attenzione; a questo punto e' necessario alimentare la Edison non solo dalla microUSB su J16)






giovedì 22 dicembre 2016

Leggere dati da Firebase Realtime Database

Per me che vengo da una esperienza SQL mettere le mani per la prima volta su un Database NoSql come quello di Firebase e' un po' spiazzante....ma ci si fa l'abitudine

Per prima cosa Firebase non gestisce i dati mentre tabelle bidimensionali a struttura rigida ma i dati vengono rapppresentati come un albero a cui sono attaccate delle derivazioni (child) i quali non e' necessario che abbiamo la stessa struttura (al di sotto di una chiave, per esempio, in un caso le proprieta' possono essere due oppure tre)


Se si editano i dati dalla Console di Firebase si vedra' che posizionando il mouse sulla root appaiono dei simboli (+ e -)..la prima volta che si usa sembra che non si abbia nessuna interazione perche' i simboli scompaiono appena si sposta il mouse



Oltre a creare i dati in modo interattivo si possono ovviamente creare anche via codice. Di seguito un esempio in Javascript commentato con le righe evidenziate in giallo
----------------------------------------------------------------
<!DOCTYPE html>
<html>
<head>
  <meta charset=utf-8 />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Read</title>

  <!-- Firebase -->
<script src="https://www.gstatic.com/firebasejs/3.6.4/firebase.js"></script>



</head>
<body>

<pre id="punti">

</pre>

<script>
  // Initialize Firebase

  var config = {
    apiKey: "AIzaSyAchx8oVeLUgxxxxxxxxxxxxxxxxxxxxxxx",
    authDomain: "notifica-23425.firebaseapp.com",
    databaseURL: "https://notifica-23425.firebaseio.com",
    storageBucket: "notifica-23425.appspot.com",
    messagingSenderId: "392xxxxxxxxxxxxxx"
  };
  firebase.initializeApp(config);


  var timestamp = new Date().getTime();
  console.log(timestamp);

  //vengono inseriti due nodi nella radice
  // usando il comando set vengono di fatto rimossi tutti i contenuti nel db
  var ins_punti = firebase.database().ref();

  ins_punti.set({
        l2: {
          lat: 1,
          lon: 30,
          nome: "Venezia",
          tempo: timestamp
        },
        l3: {
          lat: 33,
          lon: 40,
          nome: "Bari"
        }
      });

  //vengono aggiunti al nodo Punti due elementi. Si tratta di un append
  // e viene quindi di default inserita una chiave univoca
  ins_punti.child("Punti").push({
              lat: 13,
              lon: 33,
              nome: "Torino"
          });

  ins_punti.child("Punti").push({
                      lat: 14,
                      lon: 35,
                      nome: "Firenze"
                  });


  //vengono mostrate a video tutte le coppie del ramo "l2" del nodo in radice
  const prePunti = document.getElementById('punti');
  const dbRefPunti = firebase.database().ref().child('l2');
  dbRefPunti.on('value', snap => {
              prePunti.innerText = JSON.stringify(snap.val(),null,1);

  //con questa chiamata vengono richieste tutte le coppie chiave/valore del ramo Punti
  //da qui in poi i risultati sono visibili nel log e non a video
  firebase.database().ref().child('Punti').on('value', snap => console.log(snap.val()));

  //con questa chiamata vengono richieste tutte le coppie chiave/valore dell'ultimo nodo aggiunto alla lista del ramo Punti
  firebase.database().ref().child('Punti').limitToLast(1).on('child_added', snap => console.log(snap.val()));

  //con questa chiamata viene richiesto il valore del campo nome dell'ultimo nodo aggiunto alla lista del ramo Punti
  firebase.database().ref().child('Punti').limitToLast(1).on('child_added', snap => console.log(snap.val().nome));

  //con questa chiamata viene richiesto il valore del campo nome del nodo con lat=13 del ramo Punti
  firebase.database().ref().child('Punti').orderByChild("lat").equalTo(13).on('child_added', snap => console.log(snap.val().nome));

  firebase.database().ref().child('l2').update({lat: 133});

  //rimuove il ramo l1 dalla root
  firebase.database().ref().child('l3').remove();
});



</script>

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

La cosa interessante e' che le modifiche effettuate risultano essere in realtime. Se ci sono differenti client con la pagina Web aperta ed una modifica e' stata effettuata al DB questa sara' visualizzata su tutti i client senza la necesssita' di ricarica la pagina

Come per i db sql anche per Firebase e' possibile inserire la definizione delle chiavi per accelerare la ricerca e si possono definire le regole di accesso. Cio' viene effettuato nel Tab regole o rules













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...