mercoledì 8 febbraio 2017

Micro:Bit

Microbit e' un progetto promosso per introduzione all'informatica dei bambini nel Regno Unito. In estrema sintesi vengono regalati ai ragazzi di 11-12 anni delle piccole schede dotate di un processore Arm, Accelerometro, Bluetooth, un array di led 5x5, due pulsanti ..ed altre cose; con queste in classe fanno dei piccoli esperimenti

Questo esperimento non e' nuovo perche' agli inizi degli anni 80 era stato lanciato il progetto BBC Micro (basato su 6502)

Comfronto tra la scheda ed una moneta da 1 euro
In Italia la scheda si trova su Amazon a circa 25 euro nel kit con il cavo USB ed il pacco batterie



Quando si apre la confezione la cosa che salta piu' all'occhio e' l'assenza della scheda...e' talmente piccola che e' stata messa nel pacchetto di cartone sulla sinistra



Per programmare la scheda ci sono molte opzioni on line, dall'interfaccia tipo Blockly all'editor testuale. In alcuni casi e' presente anche un emulatore per provare il codice. Usare Python e' particolarmente fastidioso perche' non e' presente l'emulatore e non si puo' fare l'upload diretto del programma nella scheda (si deve compilare e scaricare il compilato e quindi trascinare il file dentro alla cartella di Micro:bit che si presenta come un disco USB)



Questa e' la IDE per Javascript con l'emulatore sulla sinistra ed il sistema di composizione dei comandi a blocchi sulla destra




Molto piu' comodo (anche se ha le sue idiosincrasie) e' usare Mu, un editor offline per MicroPython che effettua direttamente l'upload e permette alla riga di comando REPL di Python

La cosa un po' folle e' il sistema di debugging...l'errore scorre, carattere a carattere sul display...giusto per cronaca l'errore del video sottostante e'

Line 27 AttributeError module object has no attribute arcsin

sfido chiunque a capirlo al primo tentativo






Geological compass for Micro:bit

Aggiornamento qui
Un semplice programma in MicroPython per usare l'accelerometro e la bussola di Micro:bit come bussola da rilevamento geologico


Premendo il pulsante A si acquisisce la misura (se la bussola non e' stata calibrata sara' necessario farlo ruotando il dispositivo fino a creare un cerchio sul display). Il pulsante B ripete la visualizzazione dell'ultima misura
Il costo finale, compreso di batterie, e' di circa 25 euro



Il calcolo matematico di pitch e roll partendo dai dati dell'accelerometro e' stato ripreso da qui

Mark Pedley, Tilt Sensing Using a Three-AxisAccelerometer. Freescale Semiconductor Document Number: AN3461 Application Note Rev. 6, 03/2013


Il calcolo di strike e dip partendo da pitch e roll e' stato ripreso da questo articolo

 R.N. Barbosa *,1 , J.B. Wilkerson2 , H.P. Denton3 and D.C. Yoder 2, Slope Gradient and Vehicle Attitude Definition Based on Pitch and Roll Angle Measurements: A Simplified ApproachThe Open Agriculture Journal, 2012, 6, 36-40

in questa immagine l'orientazione degli assi dell'accelerometro e della bussola rispetto alla scheda (da notare che l'accelerazione Gz e' negativa perche' all'accelerometro e' saldato sulla parte opposta della scheda)




(nota : i valori di accelerazione vengono mostrati come numeri interi e non come float (m/s2) o come frazioni dell'accelerazione di gravita' (come in Android)
----------------------
from microbit import display
import microbit
import math

display.scroll("GEOCOMPASS")

cc = 180/math.pi
teta = 0
dip = 0

while True:
    if microbit.button_a.is_pressed():
        # if necessary calibrate the compass
        if not microbit.compass.is_calibrated():
            display.scroll("Calibrate compass")
            microbit.compass.calibrate()
            microbit.sleep(2500)
        gx = microbit.accelerometer.get_x()
        gy = microbit.accelerometer.get_y()
        gz = microbit.accelerometer.get_z()
        hd = microbit.compass.heading()
        # change the sign to uniform to Android
        gx = - gx
        gz = - gz
        pitch = math.atan2(gy, math.sqrt((gx*gx)+(gz*gz)))
        roll = math.atan2(-gx, gz)
        # from pitch/roll to strike/dip
        p2 = math.sin(pitch)*math.sin(pitch)
        r2 = math.sin(roll)*math.sin(roll)
        t1 = math.sqrt(p2+r2)
        # ---
        teta = (cc * math.asin(t1))
        sigma = math.asin(math.sin(roll)/t1)
        sigma = (cc * sigma)
        # -----
        # primo quadrante
        if ((gy <= 0) and (gx < 0)):
            sigma = sigma
        # secondo quadrante
        if ((gy > 0) and (gx < 0)):
            sigma = 180 - sigma
        # terzo quadrante
        if ((gy > 0) and (gx >= 0)):
            sigma = 180 - sigma
        # quarto quadrante
        if ((gy <= 0) and (gx >= 0)):
            sigma = 360 + sigma
        dip = (sigma + hd) % 360
        display.scroll("Dip " + str(int(teta)) + "/Str " + str(int(dip)))
        microbit.sleep(500)
    if microbit.button_b.is_pressed():
        # the B button repeat the last measure
        display.scroll("Dip " + str(int(teta)) + "/Str " + str(int(dip)))
----------------------

martedì 7 febbraio 2017

Integrazione GitHub e XCode

L'integrazione tra GitHub e XCode e' piuttosto semplice.
Per prima cosa dall'interfaccia Web si crea un nuovo progetto e nella root del progetto si digitano i seguenti comandi (come da istruzioni a video)

-----
git init
git add .
git commit -m "first commit"
git remote add origin https://github.com/[nome_utente]/[nome_progetto].git
git push -u origin master
-----

fatto cio' aprendo il menu Source Control di XCode si trova gia' il tutto configurato. Da qui in poi si puo' gestire tutto da XCode





lunedì 6 febbraio 2017

Io ed il guardiano : chiavi RSA

Ad un certo punto, mentre stavo rientrando nella mia citta' medievale, mi sono ritrovato chiuso fuori dalle mura. Per farmi aprire le porte dal guardiano dovevo convincere il guardiano,all'interno e che non mi poteva osservare, della mia identita'.....il tutto cio' senza consegnare le chiavi della citta' al nemico accampato li' vicino e pronto all'assalto

Ovviamente non potevo parlare perche' sarei stato ascoltato.

Il guardiano mi ha passato da sotto alla porta un foglietto con la sua chiave pubblica generata con la sequenza (la chiave privata e' rimasta custodita all'interno delle mura)
openssl genrsa -out private_key.pem 1024 (generazione chiave privata)

openssl rsa -in private_key.pem -out public_key.pem -outform PEM -pubout (la chiave privata scritta sul foglietto e' un public_key.pem)

a questo punto ero in grado di comunicare con il guardiano in modo segreto ma cio' non e' che aiuta molto perche' se avessi criptato il messaggio con il mio nome, il nemico avrebbe potuto rubare il foglio con la mia risposta e si sarebbe impossessato della mia identita'..necessita quindi un messaggio variabile che puo' essere utilizzato solo per pochissimo tempo (tipo un secondo)

Sia io che il guardiano riusciamo a vedere l'orologio della torre e ci eravamo messi d'accordo che il mio messaggio era composto dal mio codice personale e dall'ora in un determinato formato (in questo modo il messaggio sarebbe stato valido solo per un minuto)

ho proceduto quindi a creare il mio messaggio nel seguente modo
---------------------------------
#!/bin/bash
rm encrypt.dat
rm new_encrypt.txt
rm messaggio.txt
SEED=4096 #questa e' il codice identificativo persona
NOW=$(date +"%Y%H%M")
MESSAGGIO=$(($SEED * $NOW)) # il messaggio ricalcola
echo $MESSAGGIO > messaggio.txt
openssl rsautl -encrypt -inkey public_key.pem -pubin -in messaggio.txt -out encrypt.dat
---------------------------------
il guardiano ha letto il messaggio ed ha decodificato il mio codice. Ha controllato con la sua tabella e mi ha fatto entrare
---------------------------------
#!/bin/bash
openssl rsautl -decrypt -inkey private_key.pem -in encrypt.dat -out new_encrypt.txt #descritta il messaggio con la chiave privata del server
NOW=$(date +"%Y%H%M") 
TEXT=$(<new_encrypt.txt)
CHIAVE=$(($TEXT / $NOW))
echo $CHIAVE #controlla se la chiave e' presente nella lista del guardiano delle persone che possono passare
----------------------------------

con sapendo le informazioni di come formare il messaggio (con la chiave pubblica ed il metodo di combinazione del codice privato con l'orario) l'unica soluzione rimasta ai nemici chiusi fuori era quella di continuare a bussare al guardiano cercando di indovinare il codice ...peccato che il guardiano non e' uno sprovveduto ed accetta solo un foglietto sotto la porta al minuto..con codici identificativi sufficientemente lunghi si fa prima a buttare giu' la porta con la forza che a cercare di farsi aprire la serratura

venerdì 3 febbraio 2017

Sonoff

Sonoff e' una linea di prodotti IoT di ITEAD per la domotica. Ho provato ad utilizzare un interruttore WiFi basato su un sistema ESP8266


Le connessioni hanno un verso..attenzione al lato input ed output



Per interagire con Sonoff si deve utilizzare l'applicazione EWebLink.
Per prima cosa si deve premere il pulsante per almeno 10 secondi. In questo modo il dispositivo di configura in modalita' Access Point (la password e' 12345678); tramite l'applicazione e' possibili settare le configurazioni della WiFi casalinga. (il sistema di accoppiamento e' gestito da Espressif)



Modalita' HotSpot di Sonoff

Una volta configurata il dispositivo, Sonoff si aggancia alla Wifi casalinga


Il dispositivo non e' piccolissimo ma riesce ad entrare in una cassetta elettrica da muro (c'e' da verificare l'attenuazione sul segnale WiFi su un montaggio all'interno del muro)


Il pulsante nero, oltre ad essere impiegato per configurare il WiFi, funziona anche come tasto fisico per accendere e spengere la luce (pressione breve)


Un paio di considerazioni : la trasmissione dei dati avviene completamente via Cloud. Anche se Sonoff ed il telefono sono collegati sulla stessa WiFi l'attivazione avviene sempre con un sistema esterno... il che vuol dire che se il servizio Cloud viene sospeso l'interruttore cessa di funzionare

Provando a sniffare il traffico con Charles si osservano chiamate verso


  • alog.umengcloud.com/app_logs
  • eu-api.coolkit.cc:8080
  • eu-long.coolkit.cc:8080
con queste informazioni ho scoperto che qualcun altro aveva gia' fatto lo stesso lavoro (molto meglio)



Se la corrente viene a mancare l'interruttore riaggancia in modo automatico la rete WiFi, se viene persa la connessione con il WiFi l'interruttore mantiene lo stato (in pratica non si spenge automaticamente se si perde la connessione Wireless... cio' puo' essere un vantaggio od uno svantaggio..dipende dal punto di vista)

Esiste la possibilita' di riprogrammare il firmware con prodotti non ufficiali basati sul protocollo MQTT


giovedì 2 febbraio 2017

Programmazione Iwatch

Primi passi di programmazione di ioWatch di Apple


1) a differenza di Android Wear, Apple Watch non ha una connessione via cavo con cui effettuare la programmazione. La basetta magnetica infatti non ha dei pin ma effettua la ricarica in modalita' Wireless. Per questo motivo per inviare il programma allo smartwatch e' necessario che questo sia sempre accoppiato al telefono.





2) Come Android Wear, una applicazione per iWatch e' composta da due componenti. Una applicazione sul telefono (app companion) e l'applicazione sul telefono). Il progetto sara' quindi composto da due storyboard e da differenti assets



Assets della parte iWatch. Le icone sono differenti rispetto a quelle standard IOS


3) Per compilare il programma ed effettuare le prove sull'emulatore si deve cliccare in alto sulla lista dei dispositivi e selezionare lo scheme seguito da WatchKit App. Cio' ha come risultato che vengono aperti due emulatori (il primo per un IPhone 7 ed il secondo emulatore per lo smartwatch). Se invece si vuole effettuare il debug sul dispositivo fisico si deve selezionare il primo scheme (quello che riporta il solo telefono)..sara' il telefono via bluetooth ad inviare il programma verso lo smartwatch






4) il file che viene eseguito di default in un progetto IOS e' ViewController mentre in un progetto iWatch e' InterfaceController

5) anche comandi semplici come cambiare il testo di un pulsante segue sintassi differenti su IOs e su WatchKit
iOs

[_point_left setTitle:@"00" forState:UIControlStateNormal];
Watchkit 
[_point_left setTitle:@"00"];

venerdì 27 gennaio 2017

Docker su Centos 7

Ho provato a dare un'occhiata ai Container per un eventuale utilizzo parallelo a sostitutivo di alcune macchine virtuale. Quali sono le principali differenze:




Un container usa lo stesso kernel del SO principale cosi' come le librerie e la memoria e questo genera un risparmio sensibile di risorse delle macchina
In generale un Container utilizza un solo applicativo (Web Server, Sql Server) ed come regola generale i dati non sono contenuti nel container; da cio' deriva che non e' necessario delle immagini disco (con poi la necessita' di espanderle) e i Container hanno dimensioni di qualche decina di Mb e non delle centinaia di Mb come accade per le immagini

Un problema serio e' pero' il grado di isolamento. Se viene compromesso un servizio di una macchina virtuale la macchina host risulta salva. Vi sono invece notizie di compromissioni d container che permettono di attaccare il sistema operativo host

Queste sono le regole auree riportate da Red Hat

1) Don’t store data in containers
2) Don’t ship your application in two pieces
3) Don’t create large images
4) Don’t use a single layer image
5) Don’t create images from running containers (questo vuol dire di non usare i commit ma i dockerfile per creare un nuovo container)
6) Don’t use only the “latest” tag
7) Don’t run more than one process in a single container
8) Don’t store credentials in the image. Use environment variables
9) Don’t run processes as a root user
10) Don’t rely on IP addresses
per installare Docker su Centos 7 si inizia aggiungendo il repository

yum-config-manager \ --add-repo \ https://docs.docker.com/engine/installation/linux/repo_files/centos/docker.repo

e si installa la docker engine

yum -y install docker-engine makecache fast
si avvia quindi il servizio

service docker start

una volta installata la engine si inizia ad installare i container.
In generale un container si puo' installare mediante il comando pull ma se si lancia il comando run ed il container non e' installato allora sara' automaticamente installato

Per vedere  che  tutto funzioni la base e' installare questi due container

docker run hello-world
docker run docker/whalesay cowsay boo

il secondo mostra semplicemente il logo di Docker
dei container gia' pronti con le applicazioni piu' comuni si possono scaricare da Docker Store

la lista dei container si ottiene mediante
docker images

mentre il carico di lavoro mediante
docker ps

per entrare in shell sulla container si usa
docker run -i -t  [nome_container] /bin/bash

i container si stoppano con
docker stop [nome_container]

per aggiornare un container, visto che i dati risiedono nel file system normale dell'host, e' sufficiente stoppare il container, distruggerlo ed effettuare il pull della nuova immagine

Due esempi di comandi per lanciare dei servizi container
Apache (container httpd:alpine  docker pull httpd:alpine)
docker run -d -p 8080:80 --name apache -v /home/linnocenti/docker/:/usr/local/apache2/htdocs/ httpd:alpine

La prima cosa da notare e' il forwarding della porta 80 del container verso la porta 8080 dell'host (per questo il container e' raggiungibile su http://localhost:8080)
La seconda e' che i file html statici sono depositati nel filesystem dell'host (/home/linnocenti/docker)


MySql (container mysql  docker pull mysql)
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

Nel caso in cui si dovesse fare una installazione LAMP il container Httpd deve riuscire ad interagire con Mysql..questo e' effettuato mediante lo switch link




docker run --name lamp --link some-mysql -d httpd:alpine

AudioRecord su Android

Per registrare i dati raw dal microfono di Android e' necessario utilizzare AudioRecord (non MediaRecord)

quello che viene riportato in calce e' il codice per utilizzare questa libreria (e' un copia/incolla da piu' fonti...di mio non c'e' praticamente niente se non il fatto di avere messo insieme i pezzi per farlo funzionare da Android 6 in poi)

Un paio di trucchi: non e' sufficiente aggiungere i permessi di accedere al microfono (RECORD AUDIO) dentro al file  Manifest ma devono essere anche esplicitamente i permessi a livello di codice (la funzione requestRecordAudioPermission)

i dati vengono inseriti in un buffer per poi essere post processati e sono in formato di short signed (32768/-32767)


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

public class MainActivity extends AppCompatActivity {

    private Button play;
    final int SAMPLE_RATE = 8000;
    boolean mShouldContinue;
    String LOG_TAG = "Audio";
    private Button stop;


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

        requestRecordAudioPermission();

        play = (Button) findViewById(R.id.button);
        stop = (Button) findViewById(R.id.button2);

      
        play.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View view) {
                mShouldContinue = true;
                recordAudio();
            }
        });

        stop.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View view) {
                mShouldContinue = false;
            }
        });

    }


    private void requestRecordAudioPermission() {
        //check API version, do nothing if API version < 23!        int currentapiVersion = android.os.Build.VERSION.SDK_INT;
        if (currentapiVersion > android.os.Build.VERSION_CODES.LOLLIPOP){

            if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {

                // Should we show an explanation?                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {

                    // Show an expanation to the user *asynchronously* -- don't block                    // this thread waiting for the user's response! After the user                    // sees the explanation, try again to request the permission.
                } else {

                    // No explanation needed, we can request the permission.
                    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 1);
                }
            }
        }
    }

    @Override    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case 1: {
                // If request is cancelled, the result arrays are empty.                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted, yay! Do the                    // contacts-related task you need to do.                    Log.d("Activity", "Granted!");

                } else {

                    // permission denied, boo! Disable the                    // functionality that depends on this permission.                    Log.d("Activity", "Denied!");
                    finish();
                }
                return;
            }

            // other 'case' lines to check for other            // permissions this app might request        }
    }

    synchronized void recordAudio() {
        new Thread(new Runnable() {
            @Override            public void run() {
                Log.d(LOG_TAG, "Start ");

                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);

                // buffer size in bytes                int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE,
                        AudioFormat.CHANNEL_IN_MONO,
                        AudioFormat.ENCODING_PCM_16BIT);
                Log.d(LOG_TAG, "Buffer Size " + Integer.toString(bufferSize));

                if (bufferSize == AudioRecord.ERROR || bufferSize == AudioRecord.ERROR_BAD_VALUE) {
                    bufferSize = SAMPLE_RATE * 2;
                }

                short[] audioBuffer = new short[bufferSize / 2];

                AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC,
                        SAMPLE_RATE,
                        AudioFormat.CHANNEL_IN_MONO,
                        AudioFormat.ENCODING_PCM_16BIT,
                        bufferSize);

                if (record.getState() != AudioRecord.STATE_INITIALIZED) {
                    Log.e(LOG_TAG, "Audio Record can't initialize!");
                    return;
                }
                record.startRecording();

                Log.v(LOG_TAG, "Start recording");

                long shortsRead = 0;
                while (mShouldContinue) {
                    int numberOfShort = record.read(audioBuffer, 0, audioBuffer.length);
                    shortsRead += numberOfShort;



                    for(int i = 0; i < numberOfShort; i++){
                        Log.d(LOG_TAG,String.valueOf(audioBuffer[i]));                    }

                }

                record.stop();
                record.release();

                Log.v(LOG_TAG, String.format("Recording stopped. Samples read: %d", shortsRead));

            }
        }).start();
    }
}

mercoledì 25 gennaio 2017

Google Home

Nonostante non sia ancora in vendita in Italia (piu' che altro perche' manca localizzazione in italiano...su Amazon Italia si puo' comprare ma e' un po' inutile se non si e' madrelingua inglesi) ho provato ad usare l'emulatore di Google Home perche' e' possibile creare i propri comandi vocale e le proprie azioni mediante Api.ai

L'idea e' quella, un po' banale, di fare un gioco carta/sasso/forbice, usando i tre comandi vocali ed ottenendo dal sistema una risposta ovvero la contromossa vincente









Ovviamente per come e' congegnato il sistema vince sempre..



Google IO : Save the date

Google ha lanciato, a modo suo, la data del nuovo Google IO...per sapere il posto ed il luogo e' necessario risolvere 5 puzzle.

Premetto che non intendo spoilerare i risultati (anche se le soluzioni sono gia' disponibili su Internet su Reddit) ..questo e' solo un racconto dei miei due successi e degli insuccessi

Si parte da questo link http://goo.gl/oqro8L. (attenzione a disattivare uBlock e simili)
Da notare che la soluzione per ogni quiz e' una coppia di coordinate geografiche

Il primo link punta a GitHub

basta leggere il codice Javascript per ottenere la prima coordinata

Data la prima soluzione viene fornito il primo indizio e si ha il link per il secondo quesito



Il secondo passo e' un video di Youtube pubblico ma non negli elenchi. L'immagine e' fissa senza audio


Il titolo del video e' See What Cannot Be Heard
attivando i sottotitoli si ottiene la sequenza

Magic I.see_a beach,-yes._i explore daylight

Non sono riuscito a risolverlo..certo che sapere che la soluzione doveva essere una coordinata mi ha dato la certezza che la virgola non era messa li' a caso
Ecco il secondo indizio



Il terzo quiz e' decisamente piu' facile. Una radio ed un microscopio pulsante play (si tratta di una slide di una presentazione)

La voce pronuncia il seguente codice

053 050 046 049 056 050 049 044 032 050 048 046 057 057 054 053

salta subito all'occhio lo 032 centrale e sapendo che si tratta del codice di una coordinata si desume che 032 indichi la virgola separatrice. Basta un confronto col codice ASCII della virgola (32) per capire che la coordinata e' una semplice codifica ascii dei numeri
Il


ed arriviamo ai due quiz per me impossibili
il quarto puzzle rimanda ad una cartella GDrive con alcune immagini

da notare anche il proprietario delle immaigni ovvero Pei Xiu , cartografo cinese
queste sono le immagin

caves.jpg

pillar.jpg

shrine.jpg

spire.jpg

ho provato a vedere se per caso negli Exif ci fosse il geotag con le coordinate. Niente da fare.
Ho provato a cercare le immagini con TinEye ma niente da fare


con Google Image (anche con l'immagine corrotta) si arriva a capire che le immagini sono relative al tempio di Gunung Kawi


pensavo di avere vinto una volta trovate le coordinate del tempio



ma e' tutto sbagliato....non si sono andato nemmeno vicino
con l'aiuto della rete comunque questo e' il quarto indizio


ed arriviamo al quinto quiz..inutile dire che su questo non sono riuscito nemmeno a partire


e questa e' la soluzione finale


2 su 5 e' sempre meglio che 0 su 5 (impegnandomi avrei potuto fare 3 su 5 ma l'enplein era praticamente impossibile)

martedì 17 gennaio 2017

Grafico realtime dellaccelerometro su Android

Un esempio su come visualizzare dati realtime dell'accelerometro su Android


E' stata impiegata la libreria GraphView (per aggiungere la libreria, invece di usare Gradle, ho scaricato il file .jar, ho creato una directory libs nel progetto con Project Clean finale)

--------------------------------
package com.luca_innocenti.wristcoach;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import com.google.android.gms.wearable.DataMap;
import com.google.android.gms.wearable.MessageEvent;

import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.LegendRenderer;
import com.jjoe64.graphview.series.DataPoint;
import com.jjoe64.graphview.series.DataPointInterface;
import com.jjoe64.graphview.series.LineGraphSeries;


public class MainActivity extends AppCompatActivity implements SensorEventListener {

    private String receivedMessage = null;
    private static final String TAG = "WearMainActivity";

    private SensorManager sensorManager;
    private long lastUpdate;
    private double graph2LastXValue = 5d;
    private LineGraphSeries<DataPointInterface> mSeries;
    private LineGraphSeries<DataPointInterface> mSeries2;
    private LineGraphSeries<DataPointInterface> mSeries3;


    private double[] linear_acceleration = new double[3];
    private double[]  gravity = new double[3];


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

        IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND);
        MessageReceiver messageReceiver = new MessageReceiver();
        LocalBroadcastManager.getInstance(getApplication()).registerReceiver(messageReceiver, messageFilter);

        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

        lastUpdate = System.currentTimeMillis();

        GraphView graph = (GraphView) findViewById(R.id.graph);

        mSeries = new LineGraphSeries<>();
        mSeries2 = new LineGraphSeries<>();
        mSeries3 = new LineGraphSeries<>();



        mSeries.setTitle("AX");
        mSeries.setColor(Color.GREEN);
        //mSeries.setDrawDataPoints(true);        //mSeries.setDataPointsRadius(10);        mSeries.setThickness(2);

        mSeries2.setTitle("AY");
        mSeries2.setColor(Color.RED);
        //mSeries2.setDrawDataPoints(true);        //mSeries2.setDataPointsRadius(10);        mSeries2.setThickness(2);

        mSeries2.setTitle("AZ");
        mSeries2.setColor(Color.RED);
        //mSeries2.setDrawDataPoints(true);        //mSeries2.setDataPointsRadius(10);        mSeries2.setThickness(2);

        graph.addSeries(mSeries);
        graph.addSeries(mSeries2);
        graph.addSeries(mSeries3);
        graph.getViewport().setXAxisBoundsManual(true);
        graph.getViewport().setMinX(0);
        graph.getViewport().setMaxX(40);

        graph.getViewport().setYAxisBoundsManual(true);
        graph.getViewport().setMinY(-7);
        graph.getViewport().setMaxY(7);

        graph.getLegendRenderer().setVisible(true);
        graph.getLegendRenderer().setAlign(LegendRenderer.LegendAlign.TOP);


    }

    public class MessageReceiver extends BroadcastReceiver {
        private static final String TAG = "MessageReceiver";

        @Override        public void onReceive(Context context, Intent intent) {

            receivedMessage = intent.getStringExtra("message");
            Log.d("MessageReceiver", "onReceive() receivedMessage = " + receivedMessage);

            if (receivedMessage != null) {
                // Display message in UI                //message = receivedMessage;                Log.d("MessageReceiver", "receivedMessage =" + receivedMessage);
            } else {
                receivedMessage = "No Message";
                Log.d("MessageReceiver", "receivedMessage = No Message");
            }

        }
    }


    @Override    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            getAccelerometer(event);
        }

    }

    @Override    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        //To change body of implemented methods use File | Settings | File Templates.    }

    private void getAccelerometer(SensorEvent event) {
        float[] values = event.values;
        // Movement        float x = values[0];
        float y = values[1];
        float z = values[2];
        final double alpha = 0.8;

        // Isolate the force of gravity with the low-pass filter.        gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
        gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
        gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

        // Remove the gravity contribution with the high-pass filter.        linear_acceleration[0] = event.values[0] - gravity[0];
        linear_acceleration[1] = event.values[1] - gravity[1];
        linear_acceleration[2] = event.values[2] - gravity[2];

        String strTmp = "Acc.\n"                + " X: " + linear_acceleration[0] + "\n"                + " Y: " + linear_acceleration[1] + "\n"                + " Z: " + linear_acceleration[2];
        Log.d(TAG,strTmp);
        Double risultante = Math.sqrt(((linear_acceleration[0]*linear_acceleration[0])+(linear_acceleration[1]*linear_acceleration[1])+(linear_acceleration[1]*linear_acceleration[1])));

        long actualTime = System.currentTimeMillis();
        graph2LastXValue += 1d;
        mSeries.appendData(new DataPoint(graph2LastXValue, linear_acceleration[0]), true, 100);
        mSeries2.appendData(new DataPoint(graph2LastXValue, linear_acceleration[1]), true, 100);
        mSeries3.appendData(new DataPoint(graph2LastXValue, linear_acceleration[2]), true, 100);


        Log.d("luca",Double.toString(risultante));

    }



    @Override    protected void onResume() {
        super.onResume();
        sensorManager.registerListener(this,
                sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_FASTEST);
    }
    @Override    protected void onPause() {
        super.onPause();
        sensorManager.unregisterListener(this);
    }
}

lunedì 16 gennaio 2017

Integrazione di Android Studio con GitHub

Android Studio permette una buona integrazione con GitHub ma ci sono un po' di passi non banalissimi per arrivare al risultato desiderato.

Per prima cosa dall'interfaccia Web di GitHub si crea il nuovo progetto

Poi da Android Studio si Crea un Git Repository



facendolo puntare alla directory dell'applicazione in fase di sviluppo (in questo caso una applicazione per Android Wear)


Si apre un terminale da dentro Android Studio e nella root del progetto si digita il codice


git remote add origin https://github.com/[username]/[project_name].git

si conferma
In seguito si effettua il primo commit
A questo punto si e' pronti per fare il primo push del progetto verso GitHub




La richiesta della password e' quella relativa all'account GitHub (in questo caso e' pero' il portachiavi di Mac)








giovedì 12 gennaio 2017

RTC Odin OEC12C887A

Mi e' capitato di avere a che fare con un Pentium 166 con la batteria del BIOS esaurita


Facile...basta cambiarla con una nuova CR2032...peccato che non riuscivo a trovarla.
L'attenzione e' stata catturata da una componente a me sconosciuto ovvero ODIN OEC12C887A.
Da una ricerca su questo link e' emerso che Odin e' un RTC (Real Time Clock) con all'interno la batteria tampone. I componenti sono ancora in commercio ma il fatto di essere saldato sulla mother board e' una difficolta' di non poco conto.
Sul medesimo sito sono riportate tecniche, piuttosto intrusive, per far rivivere l'RTC del computer ma vista la scarsa possibilita' di successo forse e' meglio lasciare tutto com'e' ed aggiornare i parametri ad ogni accensione

LLama3 Anita

A seguito di questo post ho provato a vedere ho provato a vedere cosa accadeva ad utilizzare un modello specifico per la lingua italiana in...