venerdì 2 ottobre 2015

Soddisfazioni

Quando un amico/cliente ti manda un SMS come quello sotto riportato ti rendi conto che il tuo lavoro non e' proprio inutile (sorvoliamo sull'italiano....)


giovedì 1 ottobre 2015

Ricevitore GPS per Arduino

Sto lavorando per interfacciare Arduino con un ricevitori GPS e ne ho presi un paio, di prezzo nettamente differente, per delle prove

GY-NEO6MV2per un costo di circa una decina di euro si puo' acquistare il ricevitore GY-NEO6MV2.

Si tratta di un semplice dispositivo con 4 connessioni (VCC 2.7-5V, GND, TX ed RX). Non e' di fatto programmabile ed il modulo trasmette dati sulla seriale (9600, 8N1) in formato NMEA ad 1Hz


Per l'uso con Arduino si puo' usare la libreria TinyGPS

Avendo un cavo FTDI si puo' collegare direttamente il dispositivo al PC leggendo i dati NMEA direttamente in un emulatore di terminale





Adafruit GPS Breakout

Ad un costo di circa 4 volte superiore (40 euro) si puo' trovare il modulo GPS Breakout di Adafruit che risulta essere di un'altra classe rispetto a GY-NEO6MV2.



E' disponibile un piedino per accendere o spengere il dispositivo, e' abilitata la ricezione dei segnali SBAS/EGNOS per il posizionamento DPGS, e' possibile variare il tipo di sentenze ricevute e la velocita' con cui vengono generate (da 100 milliHz a 10 Hz) e la velocita' della seriale.

Per l'uso con Arduino si puo' usare la libreria Adafruit-GPS-Library in cui sono contenuti vari esempi

C'e' da notare un bug nel firmware. Caricando lo sketch echo (che rimanda sulla seriale le stringhe NMEA del dispositivo) compaiono dei caratteri anomali
$PGTOP,11,2*6E
$PGTOP,11,2*6E
questi sono codici di programmazione del modulo MTK3339 e non dovrebbero essere presenti nel flusso NMEA (peraltro non c'e' modo di sopprimerli, si possono eliminare solo in postprocessing)

Il modulo viene riportato per avere una assorbimento di 20 mA in modalita' operativa e dalle mie prove tale valore risulta conforme al teorico

Di seguito il codice Arduino da poter usare con Gps Adafruit con impostata la richiesta della modalita' DPGS

----------------------------------------------------------------------
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>

unsigned long delaytime=250;


SoftwareSerial mySerial(3, 2);
Adafruit_GPS GPS(&mySerial);
#define GPSECHO  true
boolean usingInterrupt = false;
void useInterrupt(boolean);



void setup() {

  Serial.begin(9600);
  GPS.begin(9600);
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   // 1 Hz update rate
  GPS.sendCommand(PGCMD_ANTENNA);
  //enable DPGS
  GPS.sendCommand(PMTK_ENABLE_SBAS);
  GPS.sendCommand(PMTK_ENABLE_WAAS);

  useInterrupt(true);
  delay(1000);
  mySerial.println(PMTK_Q_RELEASE);
}

SIGNAL(TIMER0_COMPA_vect) {
  char c = GPS.read();
#ifdef UDR0
  //if (GPSECHO)
    //if (c) UDR0 = c;
#endif
}

void useInterrupt(boolean v) {
  if (v) {
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  } else {
    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}

uint32_t timer = millis();



void loop() {
  if (! usingInterrupt) {
    char c = GPS.read();
    //if (GPSECHO)
      //if (c) Serial.print(c);
  }
  if (GPS.newNMEAreceived()) {
    if (!GPS.parse(GPS.lastNMEA()))
      return;
  }

  if (timer > millis())  timer = millis();

  if (millis() - timer > 1000) {
    timer = millis(); // reset the timer
    //NOME DELLA STAZIONE
    Serial.print("ST01-");
      
    //GPS
    Serial.print(GPS.hour,DEC);
    Serial.print(":");
    Serial.print(GPS.minute,DEC);
    Serial.print(":");
    Serial.print(GPS.seconds,DEC);
    //Serial.print(".");
    //Serial.print(GPS.milliseconds,DEC);
    Serial.print(" ");
    Serial.print(GPS.day, DEC); Serial.print('/');
    Serial.print(GPS.month, DEC); Serial.print("/20");
    Serial.print(GPS.year, DEC);
if (GPS.fix)
    {
    Serial.print("-");
    Serial.print(GPS.latitude,4);
    Serial.print("-");
    Serial.print(GPS.lat);
    Serial.print("-");
    Serial.print(GPS.longitude,4);
    Serial.print("-");
    Serial.print(GPS.lon);
    Serial.print("-");
    Serial.print(GPS.HDOP,4);
    Serial.print("-");
    Serial.print((int)GPS.fixquality);

    Serial.print("-");
    Serial.print((int)GPS.satellites);
    }
    Serial.println();
  }
}

-------------------------------------------
Il modulo puo' essere messo in stato di ibernazione con i comandi
GPS.standby()
GPS.wakeup()

Una Arduino Uno con il modulo GPS attivo consuma circa 20 mA mentre la stessa configurazione con il GPS in ibernazione scende al di sotto dei 10 mA
Ovviamente dopo aver riacceso il modulo, il sistema deve effettuare da zero il Fix GPS (e' quindi consigliabile inserire la batteria tampone per le effemeridi)

Arduino con batteria Lipo e pannello solare


Gia' in precedenza avevo provato ad usare un pannello solare per alimentare Arduino ma stavolta per un progetto piu' serio avevo necessita' di un sistema di alimentazione a batteria ricaricabile a sua volta ricaricata da un pannello solare. La batteria permette di mantenere l'operativita' quando l'irraggiamento solare e' scarso od assente


Ho quindi impiegato un solar charger shield 2.2 (di Seedstudio) accoppiato ad una batteria Lipo da 3000 mAh ed un pannello solare da 100x80 mm da 1 W. Le connessioni sono tutte con JST 2.0
La batteria puo' essere ricaricata sia dal pannello solare che da una connessione microUsb

Lo shield ha anche la possibilita' di connettere il pin A0 per misurare la quantita' di energia residua nella batteria. Cio' si ottiene cortocircuitando due connettori R7 sulla scheda (vicino ad R8 e con a destra la sigla A0)
Piu' semplice a dirsi che a farsi....come si puo' vedere c'e' stata la necessita' di fare un ponticello con un cavo extra (in verde nella foto)


per ottenere il valore in Volt della carica istantanea della batteria si procede con il seguente codice. Da notare che il calcolo del voltaggio sul pin A0 non e' quello standard per passare dal Digital Number al voltaggio per i pin analogici su Arduino (viene inserito un fattore 2 di correzione); questo non  e' un errore ed e' dovuto alla caratteristica dello shield

--------------------------------------------------
const int analogInPin = A0; 
int BatteryValue = 0;
float outputValue = 0; 

void setup() { 
    Serial.begin(9600); 

 void loop() { 
 BatteryValue = analogRead(analogInPin); 
outputValue = (float(BatteryValue)*5)/1023*2; 
Serial.print(" voltage = "); 
Serial.println(outputValue); 
Serial.println("V \n"); 
delay(1000); 
}

--------------------------------------------------
Un breve calcolo per calcolare il tempo di ricarica (dati il pannello solare da 1W/5V e la batteria da 3000 mAh, ovvero 3Ah)

Il pannello eroga 1W/5V = 0.2 A. Per la ricarica si divide 3Ah/0.2A e si ottiene il tempo di ricarica di 15  ore

mercoledì 30 settembre 2015

6 anni di Arduino


Sono passati 6 anni dal primo utilizzo di Arduino durante il dottorato di ricerca
Forse questo "Lego per adulti" mi ha un po' preso la mano a vedere quanto hardware ho a giro per casa

Scatola Arduino

Sensori/Accessori


Sensori/Accessori 2

Cavi




martedì 29 settembre 2015

Time Lapse con Xiaomi Yi

Questo time lapse e' stato creato nella notte tra sabato 26 e domenica 27 settembre come prova per filmare l'eclisse lunare del 28 settembre con l'action cam Xiaomi Yi (ero malato e non potendo lasciare casa mi sono dovuto adattare, per la ripresa, al terrazzo di casa nonostante la pessima esposizione verso Sud....la ripresa dell'eclisse e' stata annullata a priori a causa della copertura nuvolosa del giorno successivo)


Per alimentare la camera e' stato adottato un powerbank da 10000 mA (dopo 8 ore era ancora disponibile 1/4 di energia). Un'altra accortezza e' stata quella di coprire i led del Wifi e del pulsante di accensione perche' sono talmente luminosi da influenzare la ripresa nell'oscurita' della notte

I fotogrammi (uno al minuto) eseguiti alla massima risoluzione della camera sono stati montati e riscalati in formato hd720 mediante avconv con la seguente  stringa

avconv -f image2 -r 24 -i Y0020%04d.JPG -codec:v libx264 -s:v hd720 timelapse.mp4

venerdì 11 settembre 2015

Fritzing: da idea a PCB

Fritzing non e' solo un programma che permette di trascrivere in modo ordinato i collegamenti di una breadboard ma puo' essere utilizzato anche per crearsi un PCB (circuito stampato) da mettere poi in produzione


Partendo dallo schema sopra riportato, cliccando sul tab PCB. Si puo' fare in modo che il programma cerchi in modo autonomo quale sia la migliore disposizione delle piste del PCB mediante la funzione Routing/Autoinstrada. Questa e' una procedura automatica ma non e' esente da errori. Si devono spostare/routare i componenti fino a che ci si trova nella condizione si non avere piste incrociate e non sia necessario usare dei cavi esterni al di sopra del PCB (si possono creare PCB a due facce od una faccia, per uso amatoriale e' meglio a faccia singola)


Una volta ottenuto un autoinstradamento che puo' sembrare corretto, si clicca su Routing/Design Rule Check (DRC) che effettivamente effettua il controllo sulla correttezza del circuito (ci possono essere casi in cui le piste non ci incrociano ma sono comunque troppo vicine da creare interferenza)
E' un lavoro piuttosto noioso e fatto di tentativi ed errori

Alla fine, se non ci sono errori, si puo' stampare su carta il risultato
Come indicato sul sito di Fritzing e' molto utile incollare la stampa del circuito appena creato su una base di polistirolo dove inserire i componenti reali. Cio' permette di verificare direttamente l'ingombro dei componenti fisici ed eventuali errori 


giovedì 10 settembre 2015

DIY HomeKit con Android Wear e Raspberry

Questo e' un tentativo di realizzare un homekit, un sistema di domotica, che permetta di accendere dei dispositivi con un sensore di prossimita' (tipo il lampadario quando si entra in una stanza)

I componenti di questo kit sono
1) un orologio con Android Wear
2) un Raspberry (qualsiasi modello) con un dongle Bluetooth LE
3) un rele



L'orologio e' stato programmato come emettitore di beacon (e' possibile modificare il valore del minor in modo da personalizzarlo)


Di seguito il codice Android

--------------------------------------
apply plugin: 'com.android.application'

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

final def var = repositories {
    jcenter{
        url "http://jcenter.bintray.com/"    }
}
var

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.android.support:wearable:1.2.0'    compile 'com.google.android.gms:play-services-wearable:7.5.0'}
dependencies {
    compile 'org.altbeacon:android-beacon-library:2+'
} 

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

--------------------------------------
package luca_innocenti.beacontrasmitter;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.wearable.view.WatchViewStub;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.pm.PackageManager;

import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconParser;
import org.altbeacon.beacon.BeaconTransmitter;
import org.altbeacon.beacon.Identifier;

import java.util.Arrays;


@TargetApi(21)
public class MainActivity extends Activity {

    private BeaconTransmitter mBeaconTransmitter;
    private TextView testo;
    private SeekBar barra;
    private Button pulsante;
    private int stato;
    private TextView testo2;
    private TextView testo3;


    private void trasmetti(){
        if (checkPrerequisites()) {
            //            //mBeaconTransmitter = new BeaconTransmitter(this, new BeaconParser().setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));            mBeaconTransmitter = new BeaconTransmitter(this, new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
            //beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:0-3=4c000215,i:4-19,i:20-21,i:22-23,p:24-24"));  // iBeacons
            //beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));      // Estimotes
            //beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:0-3=a7ae2eb7,i:4-19,i:20-21,i:22-23,p:24-24"));  // easiBeacons
            // uuid estimote B9407F30-F5F8-466E-AFF9-25556B57FE6D
            Beacon beacon = new Beacon.Builder()
                    .setId1("B9407F30-F5F8-466E-AFF9-25556B57FE6D")
                    .setId2("999")
                    .setId3(testo.getText().toString())
                    .setManufacturer(0x015D) // Simula Estimote                    .setTxPower(-59)
                    .setDataFields(Arrays.asList(new Long[]{0l}))
                    .build();

            mBeaconTransmitter.startAdvertising(beacon);
            stato = 1;
        }

    }

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



        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override            public void onLayoutInflated(WatchViewStub stub) {
                barra = (SeekBar) stub.findViewById(R.id.seekBar);
                testo = (TextView) stub.findViewById(R.id.textView);
                testo2 = (TextView) stub.findViewById(R.id.textView2);
                testo3 = (TextView) stub.findViewById(R.id.textView3);
                pulsante = (Button) stub.findViewById(R.id.button);


                barra.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                                                     @Override                                                     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                                                         testo.setText(String.valueOf(new Integer(progress)));
                                                     }

                                                     @Override                                                     public void onStartTrackingTouch(SeekBar seekBar) {

                                                     }

                                                     @Override                                                     public void onStopTrackingTouch(SeekBar seekBar) {

                                                     }
                                                 }
                );

                pulsante.setOnClickListener(new View.OnClickListener() {
                    @Override                    public void onClick(View v) {
                        testo2.setText("Trasmitter ON");
                        testo3.setText("B9407F30-F5F8-466E-AF\nF9-25556B-57FE6D-999-"+testo.getText());
                        pulsante.setEnabled(false);
                        barra.setEnabled(false);
                        trasmetti();
                    }
                });
            }
        });
    }


    @Override    protected void onDestroy() {
        super.onDestroy();
        if (stato == 1) {
            mBeaconTransmitter.stopAdvertising();
        }
    }

    @TargetApi(21)
    private boolean checkPrerequisites() {

        if (android.os.Build.VERSION.SDK_INT < 18) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Bluetooth LE not supported by this device's operating system");
            builder.setMessage("You will not be able to transmit as a Beacon");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                @Override                public void onDismiss(DialogInterface dialog) {
                    finish();
                }

            });
            builder.show();
            return false;
        }
        if (!getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Bluetooth LE not supported by this device");
            builder.setMessage("You will not be able to transmit as a Beacon");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                @Override                public void onDismiss(DialogInterface dialog) {
                    finish();
                }

            });
            builder.show();
            return false;
        }
        if (!((BluetoothManager) getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter().isEnabled()){
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Bluetooth not enabled");
            builder.setMessage("Please enable Bluetooth and restart this app.");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                @Override                public void onDismiss(DialogInterface dialog) {
                    finish();
                }

            });
            builder.show();
            return false;

        }

        try {
            // Check to see if the getBluetoothLeAdvertiser is available.  If not, this will throw an exception indicating we are not running Android L            ((BluetoothManager) this.getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter().getBluetoothLeAdvertiser();
        }
        catch (Exception e) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Bluetooth LE advertising unavailable");
            builder.setMessage("Sorry, the operating system on this device does not support Bluetooth LE advertising.  As of July 2014, only the Android L preview OS supports this feature in user-installed apps.");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
                @Override                public void onDismiss(DialogInterface dialog) {
                    finish();
                }

            });
            builder.show();
            return false;

        }

        return true;
    }
}

--------------------------------------
Per leggere la prossimita' dell'orologio ho montato sulla Raspberry un dongle Broadcome (gia' visto qui) ed il programma in Python iBeacon-Scanner (Github) leggermente modificato

Il programma cicla fino quando non vede un dispositivo con minor=1, allora controlla l'Rssi e stima la distanza. Se la distanza e' inferiore ad un determinato valore si puo' agire sulle porte GPIO ed attivare il rele' (funzione da implementare ma di facilissima realizzazione)

--------------------------------------
import blescan
import sys

import bluetooth._bluetooth as bluez

dev_id = 0
try:
    sock = bluez.hci_open_dev(dev_id)
    #print "ble thread started"

except:
    print "error accessing bluetooth device..."
        sys.exit(1)

blescan.hci_le_set_scan_parameters(sock)
blescan.hci_enable_le_scan(sock)

while True:
    returnedList = blescan.parse_events(sock, 10)
    #print "----------"
    for beacon in returnedList:
        #print beacon
        valori = beacon.split(",")
        mac = valori[0]
        uuid = valori[1]
        major = valori[2]
        minor = valori[3]
        rssi = valori[4]
        rssi2 = valori[5]
        #print minor
        if (minor == "1"):
            #print minor+" "+rssi2
            if (int(rssi2) > -75):
                print "vicino"
            else:
                print "lontano"
--------------------------------------

Aruco Tag e filtri Kalman

Usando gli Aruco tag in ambiente esterno con illuminazione solare a diverse ore e in diversi periodi dell'anno ho trovato un errore nell...