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

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

martedì 10 gennaio 2017

Message Api in Android Wear

Le Message API permettono di scambiare messaggi tra dispositivi Android, nel caso specifico tra il telefono e l'orologio mediante Android Wear
Nell'esempio in esame vengono sincronizzati due NumberPicker



Il progetto completo e' disponibile su GitHub ed e' basato sull'esempio a questo link

Per NumberPicker sul telefono, piu' che altro per la gestione del font, e' stato usato questa modifica



Lato Telefono
build.gradle
--------------------------------------
apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.luca_innocenti.apticmetro"
        minSdkVersion 21
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    wearApp project(':wear')
    compile 'com.google.android.gms:play-services:10.0.1'
    compile 'com.android.support:appcompat-v7:25.1.0'
    compile 'com.shawnlin:number-picker:2.3.0'

    testCompile 'junit:junit:4.12'
}
--------------------------------------

MainActivity
--------------------------------------
package com.luca_innocenti.apticmetro;

import android.icu.lang.UCharacter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
//import android.widget.NumberPicker;
import com.shawnlin.numberpicker.NumberPicker;

import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;



// Applicazione Mobile
public class MainActivity extends AppCompatActivity implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener
{

    private static final String TAG = "MainActivity";
    private GoogleApiClient googleApiClient;

    private ImageButton play;
    private NumberPicker bpm;
    private int counter;

    public MainActivity() {
    }


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

        googleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

        //play = (ImageButton) findViewById(R.id.imageButton);
        bpm = (NumberPicker) findViewById(R.id.number_picker);


        bpm.setMinValue(40);
        bpm.setMaxValue(150);
        bpm.setValue(80);
        bpm.setWrapSelectorWheel(false);


        bpm.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            @Override
            public void onValueChange(NumberPicker picker, int oldVal, int newVal){
                //Display the newly selected number from picker
                Log.d(TAG,Integer.toString(newVal));
                String sendMessage = Integer.toString(newVal);
                new SendToDataLayerThread("/path", sendMessage).start();
            }
        });



        /*play.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //Toast.makeText(MainActivity.this, "Inviato", Toast.LENGTH_SHORT).show();
                String sendMessage = "Start";
                counter++;

                new SendToDataLayerThread("/path", sendMessage).start();

                Log.d(TAG, "SendToDataLayerThread()");


            }
        });*/
    }


    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart()");
        googleApiClient.connect();
    }

    // data layer connection
    @Override
    public void onConnected(Bundle bundle) {
        Log.d(TAG, "onConnected()");
    }

    // Activity stop
    @Override
    protected void onStop() {
        if (null != googleApiClient && googleApiClient.isConnected()) {
            googleApiClient.disconnect();
        }
        super.onStop();

        Log.d(TAG, "onStop()");

    }

    @Override
    public void onConnectionSuspended(int cause) { }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) { }

    private class SendToDataLayerThread extends Thread{
        String path;
        String handheldMessage;

        public SendToDataLayerThread(String pth, String message) {
            //path = "/messaggio";
            path = pth;
            handheldMessage = message;
        }
        public void run() {
            Log.d(TAG, "SendToDataLayerThread()");

            NodeApi.GetConnectedNodesResult nodeResult = Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
            for (Node node : nodeResult.getNodes()) {
                MessageApi.SendMessageResult result =
                        Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), path, handheldMessage.getBytes()).await();

                if (result.getStatus().isSuccess()) {
                    Log.d(TAG, "To: " + node.getDisplayName());
                    Log.d(TAG, "Message = " + handheldMessage );
                }
                else {
                    Log.d(TAG, "Send error");
                }
            }
        }
    }
}
--------------------------------------


AndroidManifest.xml
--------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.luca_innocenti.apticmetro">

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
--------------------------------------



Lato Orologio 
build.gradle
--------------------------------------
apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.luca_innocenti.apticmetro"
        minSdkVersion 21
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.google.android.support:wearable:2.0.0-beta1'
    compile 'com.google.android.gms:play-services-wearable:10.0.1'
    provided 'com.google.android.wearable:wearable:2.0.0-beta1'
}
--------------------------------------

Manifest.xml
--------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.luca_innocenti.apticmetro">

    <uses-feature android:name="android.hardware.type.watch" />

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE" />

    <uses-library android:name="com.google.android.wearable" android:required="false" />



    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@android:style/Theme.DeviceDefault">
        <uses-library
            android:name="com.google.android.wearable"
            android:required="false" />


        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.DeviceDefault.Light">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".WearService">
            <intent-filter>
                <action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
                <action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
                <data android:scheme="wear" android:host="*" android:pathPrefix="/path" />
            </intent-filter>
        </service>
    </application>


</manifest>
--------------------------------------

MainActivity
--------------------------------------
package com.luca_innocenti.apticmetro;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Vibrator;
import android.support.v4.content.LocalBroadcastManager;
import android.support.wearable.activity.WearableActivity;
import android.support.wearable.view.BoxInsetLayout;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.NumberPicker;
import android.os.Handler;

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



// Lato orologio
public class MainActivity extends WearableActivity {

    private BoxInsetLayout mContainerView;
    private NumberPicker bpm;
    private ImageButton play;
    private int stato;
    long startTime = 0;
    long valore_bpm;

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




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

        Log.v(TAG, "onCreate()");
        IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND);
        MessageReceiver messageReceiver = new MessageReceiver();
        LocalBroadcastManager.getInstance(getApplication()).registerReceiver(messageReceiver, messageFilter);


        final Handler timerHandler = new Handler();
        final Runnable timerRunnable = new Runnable() {

            @Override
            public void run() {
                Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                vibrator.vibrate((100));
                timerHandler.postDelayed(this, valore_bpm);
            }
        };


        stato = 0;

        mContainerView = (BoxInsetLayout) findViewById(R.id.container);
        bpm = (NumberPicker) findViewById(R.id.numberPicker);
        play =(ImageButton) findViewById(R.id.imageButton);

        bpm.setMinValue(40);
        bpm.setMaxValue(150);
        bpm.setValue(80);
        bpm.setWrapSelectorWheel(false);

        play.setOnClickListener(new View.OnClickListener()
        {
            public void onClick(View v)
            {
                Log.d("Aptic","BPM "+bpm.getValue());
                valore_bpm = Math.round((60.0/bpm.getValue())*1000.0);
                Log.d("Valore bmp millisecondi","BMP3 "+valore_bpm);
                if (stato == 0) {
                    play.setImageResource(R.drawable.stop_icon);
                    stato = 1;
                    startTime = System.currentTimeMillis();
                    //Toast.makeText(MainActivity.this, Long.toString(valore_bpm), Toast.LENGTH_SHORT).show();
                    timerHandler.postDelayed(timerRunnable, 0);
                }
                else
                {
                    play.setImageResource(R.drawable.play_icon);
                    stato = 0;
                    timerHandler.removeCallbacks(timerRunnable);

                }
            }
        });

    }

    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);
                bpm.setValue(Integer.parseInt(receivedMessage));
            } else {
                receivedMessage = "No Message";
                Log.d("MessageReceiver", "receivedMessage = No Message");
            }


        }
    }




    @Override
    public void onEnterAmbient(Bundle ambientDetails) {
        super.onEnterAmbient(ambientDetails);
        updateDisplay();
    }

    @Override
    public void onUpdateAmbient() {
        super.onUpdateAmbient();
        updateDisplay();
    }

    @Override
    public void onExitAmbient() {
        updateDisplay();
        super.onExitAmbient();
    }

    private void updateDisplay() {
        if (isAmbient()) {
            mContainerView.setBackgroundColor(getResources().getColor(android.R.color.black));
            play.setVisibility(View.INVISIBLE);
        } else {
            mContainerView.setBackground(null);
            play.setVisibility(View.VISIBLE);
        }
    }




}
--------------------------------------

WearService
--------------------------------------
package com.luca_innocenti.apticmetro;

import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import com.google.android.gms.wearable.DataEventBuffer;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.WearableListenerService;

/**
 * Created by lucainnocenti on 05/01/17.
 */

public class WearService extends WearableListenerService {
    private static final String TAG = "WearService";

    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {
        super.onDataChanged(dataEvents);
        Log.v(TAG, "onDataChanged" );
    }

    @Override
    public void onMessageReceived(MessageEvent messageEvent) {
        super.onMessageReceived(messageEvent);
        Log.v(TAG, "onMessageReceived" );

        if (messageEvent.getPath().equals("/path")) {

            final String message = new String(messageEvent.getData());

            Log.d(TAG, "Message path received on watch is: " + messageEvent.getPath());
            Log.d(TAG, "Message received on watch is: " + message);

            // Broadcast message to wearable activity for display
            Intent messageIntent = new Intent();
            messageIntent.setAction(Intent.ACTION_SEND);
            messageIntent.putExtra("message", message);
            LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);

        }
        else {
            super.onMessageReceived(messageEvent);
        }
    }

}





lunedì 9 gennaio 2017

Dentro ad Amazon Fire

La bambina e' riuscita, facendo cadere di piatto lato schermo il tablet per terra, ad aprirlo in un colpo solo


Una occasione per vedere come e' fatto dentro. Curiosa la posizione dello speaker (mono)
PS: il tablet e' sopravvissuto...e' decisamente resistente

mercoledì 4 gennaio 2017

Mandelbrot su Pebble

Visto che sono riuscito ad installare SDK Pebble perche' non provarlo con un classico??



Per il codice mi sono basato su altro esempio di Mandelbrot su Pebble presente su GitHub (peraltro fatto molto meglio del mio)

La gestione dello schermo non e' banalissima perche' e' presente una gerarchia di livelli sovrapposti.

Una cosa significativa: non e' possibile avere dati di debug in formato float (non ne capisco il motivo ma e' cosi')

---------------------------------------------
#include <pebble.h>

static Window *schermo;
static Layer *render_layer;

static void disegna(Layer *layer, GContext *ctx)
{
float re_min = -2.0;
float im_min = -1.2;
float re_max = 1.0;
float im_max = 1.2;
int r = 0;

int itera = 40;
int xres=0;
int yres=0;

float a,b,x,y,x_new,y_new;
int i,j,k;

GRect layer_bounds = layer_get_bounds(layer);

xres=layer_bounds.size.w;
yres=layer_bounds.size.h;

a = 0.0;
b = 0.0;
x_new = 0.0;
y_new = 0.0;

float re_factor = (re_max-re_min);
float im_factor = (im_max-im_min);

// scrive messaggi di debug che possono essere letti in shell con il comando
// pebble logs
APP_LOG(APP_LOG_LEVEL_DEBUG,"WIDTH=%d HEIGHT=%d",xres,yres);

for (i=0;i<yres;i++)
    {
    a = re_min+(i*re_factor/yres);
    for (j=0;j<xres;j++)
{

b = im_min+(j*im_factor/xres);
x = 0.0;
y = 0.0;
//APP_LOG(APP_LOG_LEVEL_DEBUG,"X=%d Y=%d",j,i);
for (k=1;k<itera;k++)
{
x_new = (x*x)-(y*y)+a;
y_new = (2*x*y)+b;
if (((x_new*x_new)+(y_new*y_new))>4)
   {
   r = k%2;
   //APP_LOG(APP_LOG_LEVEL_DEBUG,"K=%d R=%d",k,r);
   if (r == 1)
{
GPoint punto = GPoint(j,i);
//window_set_backgroung_color(&windows,GColorBlack);
graphics_draw_circle(ctx,punto,1);
}
   break;
   }
x = x_new;
y = y_new;
}
}
    }

}

static void carica_schermo(Window *window)
{
APP_LOG(APP_LOG_LEVEL_DEBUG,"CARICA SCHERMO");
Layer *window_layer = window_get_root_layer(window);
GRect bounds = layer_get_bounds(window_layer);
render_layer = layer_create(bounds);
layer_set_update_proc(render_layer,disegna);
layer_add_child(window_layer, render_layer);
}

static void scarica_schermo(Window *window){
}

static void init(void){
APP_LOG(APP_LOG_LEVEL_DEBUG,"INIT");
schermo = window_create();
#ifdef PBL_SDK_2
window_set_fullscreen(schermo,true);
window_set_backgroung_color(schermo,GColorWhite);
#endif

window_set_window_handlers(schermo,(WindowHandlers){
   .load = carica_schermo,
   .unload = scarica_schermo,
   });
const bool animated=true;
window_stack_push(schermo,animated);
}

static void deinit(void)
{
window_destroy(schermo);
}

int main(void) {
  APP_LOG(APP_LOG_LEVEL_DEBUG,"MAIN");
  init();
  APP_LOG(APP_LOG_LEVEL_DEBUG,"%p",schermo);
  app_event_loop();
  deinit();
}
---------------------------------------------

e visto che ci siamo perche' non provare a pubblicarla sullo store di Pebble (non e' necessario un pagamento)



ed infine la prova reale (tempo di calcolo circa 6 secondi)


martedì 3 gennaio 2017

Pebble SDK su Centos 7

Colpo di genio del nuovo anno : comprare un Pebble (usato) e scoprire un paio di giorni dopo che Pebble e' stato acquistato da FitBit e che quindi dismetteranno tutto il supporto compreso il cloud !!

Mi sono quindi affrettato almeno a configurarmi l'SDK di Pebble prima che diventi non disponibile



Le istruzioni riportate sul sito sono per distribuzioni Linux basate su apt ma ho provato lo stesso l'installazione su Centos
Il pacchetto dell'SDK 64 di Pebble si scarica da qui

-----------------------------
mkdir ~/pebble-dev/
cd ~/pebble-dev/
tar -jxf ~/Downloads/pebble-sdk-4.5-linux64.tar.bz2
echo 'export PATH=~/pebble-dev/pebble-sdk-4.5-linux64/bin:$PATH' >> ~/.bash_profile
. ~/.bash_profile

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

si deve poi installare pip ed virtualenv di Python (e nessun problema)
---------------------------
cd ~/pebble-dev/pebble-sdk-4.5-linux64 
virtualenv --no-site-packages .env 
source .env/bin/activate 
pip install -r requirements.txt 
deactivate
---------------------------
in questo caso deactivate mi ha dato errore ma non ha creato problemi per i passi futuri
Vengono scaricati diversi pacchetti ma non sono riuscito a trovare una opzione per configurare il proxy server.
Si devono poi installare le dipendenze per il compilare. Nessun problema per installare libpixman e libsdl (peraltro gia' presenti)  ma su Centos non esiste un pacchetto precompilato per la libreria libfdt e si deve quindi partire dai sorgenti.
Si scarica il file tgz da GitHub e si lancia

make

nella sottodirectory libfdt viene creata la libreria libfdt-1.3.0.so. Il problema e' che l'emulatore di Pebble ricerca la libreria libfdt.so.1. Quindi e' necessario o rinominare il file oppure creare un symlink con il nome richiesto.
Usanso make install non funziona e quindi ho copiato a mano la libreria compilato in /usr/lib e /usr/lib64

L'installazione delle applicazioni sul telefono non avviene mediante il cavo, che di fatto e' funzionale solo alla ricarica, ma passa attraverso il telefono a cui e' accoppiato l'orologio


Si deve quindi attivare il pulsante a scorrimento. Non funziona con il telefono in modalita' HotSpot ma in associazione ad un vero e proprio Access Point


per creare un nuovo progetto (Javascript o C) si usa
pebble new-project [--simple] [--javascript] [--worker] [--rocky] NAME

che genera un template su cui iniziare a lavorare

pebble build per compilare

ed infine per installare si usa
pebble install --phone IP (per il telefono)
pebble install --emulator basalt (per l'emulatore. i nomi dei vari dispositivi sono riportati qui sotto)




Emulatore in esecuzione
Per interagire con l'emulatore si usano i tasti freccia

freccia su = Up
freccia giu' = Down
freccia destra = Select
freccia sinistra = Back

Per visualizzare i log si usa

pebble log

per fare piu' in fretta si puo' fare
pebble wipe & pebble build & pebble install --emulator diorite --logs