venerdì 29 marzo 2019

Dragino LG01 Lora Gateway su TheThingsNetwork

Un gateway Lora e' un dispositivo che legge il traffico Lora e lo rilancia su Internet e nel caso sul cloud di TheThingsNetwork (abbreviato TTN)
Ci sono molti Gateway Lora compreso quello originale Arduino ma a causa dei costi (Lora Gateway Arduino costa circa 350 euro) ho preso Dragino LG01, a canale singolo 868 MHz (standard Italia) che e' molto simile ad una Arduino Yun (si  programma via Arduino IDE con la libreria Bridge)


Le istruzioni ufficiale si trovano al link seguente
https://wiki.dragino.com/index.php?title=Connect_to_TTN#Create_TTN_account

(il manuale completo si trova qui)

Ma prima di iniziare si deve creare un account se TheThingsNetwork, andare nella console a creare un Gateway


Per il codice identificativo si puo'utilizzare il Mac Address (riempiendo i byte rimanenti per arrivare a 16 con FF) ed impostando Legacy Packet Forwarder

Successivamente ci si collega alla WiFi aperta del Dragino al 10.130.1.1 (od alla porta LAN) con le credenziali root/dragino. Il sistema di base e' OpenWRT per cui e' semplice configurare l'access point in WPA e controllare che il gateway sia connesso ad Internet


Si va poi sul Dragino e si scarica da qui  il file single_pkt_fwd_v004.hex. Attenzione : il file sul server si chiama single_pkt_fwd_v004.ino.hex... ho dovuto rinominare il file
Poi si va Sensor/Flash MCU, si seleziona il file sopra indicato e si aggiorna. Da questa pagina si puo' controllare che il caricamento sia avvenuto in modo corretto



Si imposta la radio sullo standard europeo


Si configura il Lora Gateway per dialogare con TheThingsNetwork indicando l'indirizzo router.eu.thethings.network, ed il Gateway ID che e' riportato nella consolle di TTN (ma senza i primi 4 caratteri ovvero senza eui-)




e si seleziona LorWan come IoT server


Se tutto e' andato bene sulla consolle di TTN il gateway sara' visto come online 

giovedì 28 marzo 2019

MPU9250 9DOF e filtro Madgwick

La MPU9250 e' un dispositivo con accelerometro, giroscopio e magnetometro triassiale


Per interfacciare l'MPU9250 ho usato la libreria https://github.com/sparkfun/MPU-9250_Breakout che funziona solo con schede Arduino con processore SAM (quindi MKR)

Nello sketch degli esempi e' stato aggiunto il filtro Madgwick

5852 dati

Pitch (filtrato)
media 60.475°
std 0.22°
skew -0.597

Roll (filtrato)
media 50.471°
std 0.34°
skew -0.113

Yaw (filtrato)
media 209.56°
std 0.54°
skew 0.2


---------------------------------------------------------------------------
#include <SparkFunMPU9250-DMP.h>
#include  <MadgwickAHRS.h> 



MPU9250_DMP imu;
Madgwick  MadgwickFilter;


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

   MadgwickFilter.begin(100);  // 100 Hz

  if (imu.begin() != INV_SUCCESS)
  {
    while (1)
    {
      Serial.println("Unable to communicate with MPU-9250");
      Serial.println("Check connections, and try again.");
      Serial.println();
      delay(5000);
    }
  }


  imu.setSensors(INV_XYZ_GYRO | INV_XYZ_ACCEL | INV_XYZ_COMPASS);


  imu.setGyroFSR(2000); // Set gyro to 2000 dps
  // Accel options are +/- 2, 4, 8, or 16 g
  imu.setAccelFSR(2); // Set accel to +/-2g
  // Note: the MPU-9250's magnetometer FSR is set at 
  // +/- 4912 uT (micro-tesla's)

  // setLPF() can be used to set the digital low-pass filter
  // of the accelerometer and gyroscope.
  // Can be any of the following: 188, 98, 42, 20, 10, 5
  // (values are in Hz).
  imu.setLPF(5); // Set LPF corner frequency to 5Hz

  // The sample rate of the accel/gyro can be set using
  // setSampleRate. Acceptable values range from 4Hz to 1kHz
  imu.setSampleRate(10); // Set sample rate to 10Hz

  // Likewise, the compass (magnetometer) sample rate can be
  // set using the setCompassSampleRate() function.
  // This value can range between: 1-100Hz
  imu.setCompassSampleRate(10); // Set mag rate to 10Hz

  imu.dmpBegin(DMP_FEATURE_GYRO_CAL |   // Enable gyro cal
              DMP_FEATURE_SEND_CAL_GYRO,// Send cal'd gyro values
              10);         
}

void loop() 
{

  if ( imu.dataReady() )
  {
    
    imu.update(UPDATE_ACCEL | UPDATE_GYRO | UPDATE_COMPASS);
    printIMUData();
  }
}

void printIMUData(void)
{  

  float accelX = imu.calcAccel(imu.ax);
  float accelY = imu.calcAccel(imu.ay);
  float accelZ = imu.calcAccel(imu.az);
  float gyroX = imu.calcGyro(imu.gx);
  float gyroY = imu.calcGyro(imu.gy);
  float gyroZ = imu.calcGyro(imu.gz);
  float magX = imu.calcMag(imu.mx);
  float magY = imu.calcMag(imu.my);
  float magZ = imu.calcMag(imu.mz);

  MadgwickFilter.update(accelX,accelY,accelZ,gyroX,gyroY,gyroZ,magX,magY,magZ) ;

  
  Serial.print(String(accelX) + "," +String(accelY) + "," + String(accelZ));
  Serial.print("," + String(gyroX) + "," + String(gyroY) + "," + String(gyroZ) + ",");
  Serial.print(String(magX) + "," + String(magY) + "," + String(magZ)+ "," );
  Serial.print(String(MadgwickFilter.getPitch())+ "," +String(MadgwickFilter.getRoll())+ "," +String(MadgwickFilter.getYaw()));
  Serial.println();
}

HP 82240B Stampante termica

La mia calcolatrice HP 48GX ha trovato la sua sorellina, la stampante termica ad infrarossi HP 82240B, perfettamente funzionante




Occupazione memoria Arduino Uno

Frugando il lavoro di altri programmatori ho trovato la seguente funzione per Arduino Uno

---------------------------------------------------------
int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
---------------------------------------------------------

Da questo link si puo' leggere che la funzione riporta lo spazio di memoria Ram del Atmega328 libero.

Usando questa funzione si puo' osservare come il compilatore ottimizzi il codice. Se per esempio viene dichiarata una stringa ma non viene utilizzata, il compilatore non alloca spazio di memoria per la stringa

Un altro trucco e' il seguente

Serial.print("luca");

occupa 5 byte (4 caratteri + null) mentre

Serial.print(F("luca"));

non occupa spazio in RAM perche' la stringa viene spostata nella flash memory e non nella ram. Questo trucco funziona per le stringhe statiche .. Serial.print(F(variabile_stringa)) usa lo spazio Ram

Lo sketch di seguito riportato indica uno spazio libero di 1819 bytes.
----------------------------------------------------------------------
//char str[] = "Hello, world!";

void setup() {
    Serial.begin(115200);
}


void loop() {
    //Serial.println(str);
    Serial.println(freeRam());
    delay(10000);
}

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

mercoledì 27 marzo 2019

Gps Kalman Filter con Arduino Uno

Partendo da questo link volevo provare a testare il filtro Kalman sui dati di un GPS ma privo della contributo di una IMU (come fatto qui)

La libreria Arduino utilizzata (questo il link) e' una conversione di un codice in C originario

Mettendo a confronto la linea blu dei dati non filtrati con quella rossa dei dati filtrati e lasciando ferma l'antenna GPS si osserva che il filtro funziona ma non limita le tipiche fluttuazioni (anche molto significative) del segnale GPS




Questo test e' stato effettuato con due antenne a basso costo: un primo modulo senza marca di derivazione cinese ed una antenna con predisposizione per PixHawk denominata gygpsv5 con a bordo un Ublox NEO6M-V2


Utilizzando il codice scaricato da Internet senza particolari modifiche tutto funzionava con il modulo cinese ma non con Ublox nonostante i messaggi NMEA fossero correttamente ricevuti da Arduino.

Salvando i dati si capisce il motivo

Neo 6P
$GLGSV,3,3,09,88,20,181,*56
$GNGLL,4347.03855,N,01113.80955,E,132645.00,A,A*7C
$GNRMC,132646.00,A,4347.03855,N,01113.80955,E,0.030,,270319,,,A*6B
$GNVTG,,T,,M,0.030,N,0.055,K,A*3E
$GNGGA,132646.00,4347.03855,N,01113.80955,E,1,08,1.35,42.1,M,45.5,M,,*76
$GNGSA,A,3,12,24,25,32,,,,,,,,,2.61,1.35,2.23*1E
$GNGSA,A,3,68,86,77,87,,,,,,,,,2.61,1.35,2.23*12

GPS
$GPGSV,2,2,08,32,33,290,40,14,26,308,23,31,04,310,,34,,,*47
$GPRMC,133332.000,A,4347.0429,N,01113.8140,E,0.21,175.07,270319,,,A*60
$GPGGA,133333.000,4347.0428,N,01113.8140,E,1,5,1.55,36.1,M,47.6,M,,*6E
$GPGSA,A,3,12,25,24,29,32,,,,,,,,1.83,1.55,0.98*00
$GPGSV,2,133,37,25,58,293,33,24,46,141,33,29,37,207,25*75
$GPGSV,2,2,08,32,33,290,39,14,26,308,22,31,04,310,,34,,,*48
$GPRMC,133333.000,A,4347.0428,N,01113.8140,E,0.35,175.07,270319,,,A*65
$GPGGA,133334.000,4347.0427,N,01113.8141,E,1,5,1.55,36.1,M,47.6,M,,*67
$GPGSA,A,3,12,25,24,29,32,,,,,,,,1.83,1.55,0.98*00


Il modulo cinese usa delle frasi NMEA che iniziano per GP***** mentre Ublox usa frasi che usaon un prefisso GN****. La libreria TinyGPS riconosce solo il primo prefisso e quindi non sa come trattare i dati dell'Ublox ...e' stato sufficiente editare la libreria sostituendo le stringhe per rendere compatibile TinyGPS con UBlox.....ma come mai questa differenza

GN e' un prefisso generico che si usa per qualunque sorgenti date (Gps americano, Beidu, Glonass etc) mentre il prefisso GP e' relativo solo al GPS americano. (Glonass per esempio ha il prefisso GL).

-----------------------------------------------------------------
#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <GPSFilter.h>

#define RXPIN 2
#define TXPIN 3

#define GPSBAUD 9600

TinyGPS gps;
SoftwareSerial uart_gps(RXPIN, TXPIN);

GPSFilter f(2.5);

unsigned long lastUpdate;
unsigned long lastSentence;
float c;

void setup()
{
 Serial.begin(9600);
 uart_gps.begin(GPSBAUD);
 lastSentence = millis();
 lastUpdate = millis();
}


void loop()
{
 while(uart_gps.available()) {    
   char c = uart_gps.read();
    
   if(gps.encode(c)) {
     int year;
     byte month, day, hour, minute, second, hundredths;
     unsigned long age;
     gps.crack_datetime(&year,&month,&day,&hour,&minute,&second,&hundredths,&age); //Get the date from the GPS
     unsigned long date; 
     gps.get_datetime(&date,0,0);
     if(date == TinyGPS::GPS_INVALID_DATE){
        Serial.println(F("Invalid Age"));
         continue;
     }
     if(age == TinyGPS::GPS_INVALID_AGE) {
         Serial.println(F("Waiting for more valid data"));
         continue;
     }
     float latitude, longitude;
     double dlat,dlong;
     gps.f_get_position(&latitude, &longitude);

     double ulat,ulong;
     ulat = (double)latitude;
     ulong = (double)longitude;
     c = (millis() - lastSentence)/1000;
     f.update_velocity2d(ulat,ulong,c);
     lastSentence = millis();
  
     if((millis() - lastUpdate) >= 500 || lastUpdate == NULL){
          f.get_lat_long(&dlat,&dlong);
         Serial.print(latitude,8);
         Serial.print(";");
         Serial.print(longitude,8);
         Serial.print(";");
         Serial.print(dlat,8);
         Serial.print(";");
         Serial.println(dlong,8);     
        
         }
          lastUpdate = millis();
   }
 }
}

Digispark ATTIny85 con Arduino IDE

Per un progettino a basso costo ed in cui necessita una sola porta digitale ho voluto provare qualcosa di differente dalla solita Arduino Uno ovvero il modulo Digispark ATTiny85

Il modulo, che costa circa 1 euro, presenta oltre al microcontrollore anche un regolatore di tensione 78M05 e la possibilita' di programmarezione via USB con Arduino IDE


Il consumo e' attestato intorno ai 20 mA


Sono disponibili 8K di memoria, I2C. SPI, ADC su 4 pin, tutti i pin possono essere usati come pin digitali

  • Pin 0 → I2C SDA, PWM (LED on Model B)
  • Pin 1 → PWM (LED on Model A)
  • Pin 2 → I2C SCK, Analog In
  • Pin 3 → Analog In (also used for USB+ when USB is in use)
  • Pin 4 → PWM, Analog (also used for USB- when USB is in use)
  • Pin 5 → Analog In

Per settare la Arduino IDE si inserisce questo link nelle additional boards

http://digistump.com/package_digistump_index.json
e dal Boards Manager si installa Digistump  AVR Boards

Tra gli sketch di esempio vi sono molte proposte. 
La cosa importante da ricordare e' si deve premere il pulsante di Upload dello sketch con la scheda rimossa dalla porta USB. Questa deve essere inserita solo quando viene richiesto dalla IDE


martedì 26 marzo 2019

Frugando nei cassetti dell'ufficio.....

e' saltato fuori un vecchio navigatore da tempo abbandonato a causa della mancanza di aggiornamenti delle mappe (l'ultimo aggiornamento presente e' del 2008)



Cosa farsene ai nostri giorni?? Leggendo ho scoperto che questo dispositivo, insieme a molti altri della stessa epoca, erano dotati di Windows CE ma effettuavano direttamente il boot nella applicazione navigatore. Tramite l'applicazione MioPocket si puo' riprendere il controllo della macchina semplicemnente utilizzando una scheda SD da inserire nello slot di espansione



Desktop Windows  CE
In fondo e' sostanzialmente inutilizzabile. Lo schermo da 5 pollici e l'assenza di una virtual keyboard (io per lo meno non l'ho trovata) rendendo l'interfaccia quasi inutilizzabile

Proprieta'

La cosa interessante e' che basta rimuovere la scheda SD e riavviare il sistema per ripristinare le funzioni di fabbrica


Una nota conclusiva....sono passati meno di 10 anni da quando questo navigatore e; stato messo in disparte da modelli piu' performanti e gia' in rete e' difficile trovare il software 

lunedì 25 marzo 2019

Confronto consumi tra Arduino Uno ed Arduino Zero

Un confronto tra i consumi di una Arduino Uno ed una Arduino Zero, entrambe originali, entrambe alimentate via USB e con lo sketch Blink in esecuzione

Arduino Zero
In casi in cui il fattore consumo sia critico e' evidente come sia preferibile la scelta di una Arduino Zero
Arduino Uno

domenica 24 marzo 2019

Filtro EKF con GPS ed IMU su Android

Per continuare la sperimentazione sui filtri applicati ai sensori ho provato ad usare l' Extended Kalman Filter, un metodo matematico che permette la fusione di dati GPS e derivanti da IMU (per una descrizione abbastanza semplice del metodo si puo' andare qui,

Visto che la matematica non e' banale ho usato un progetto gia' fatto basato su Android. A questo link si trova la descrizione che usa la fusione di velocita' e dati geografici per ridurre le fluttuazioni del segnale GPS. Una libreria EKF per Arduino si trova a questo link basato su questo codice Matlab (l'esempio che usa EKF per GPS si trova nella sottodirectory /extras/c/ con dati di pseudorange, non direttamente lat/lon). Per una breve trattazione matematica invece qui

Un altro sito con una trattazione matematica semplice si trova qui

E' disponibile una app per Android a questo indirizzo GitHub


La prova e' stata effettuata mantenendo il sensore fermo e leggendo ad intervalli di tempo la distanza cumulata (ovvero la somma degli errori dei falsi movimenti registrati dal sensore).
Si evidenzia che il tempo di campionamento del GPS e del'accelerometro sono molto differenti e gli aggiornamento del filtro sono scalati sul sensore piu' lento

Distanza cumulata in metri
Come si vede il filtro impiega circa 100 secondi per stabilizzzarsi

Eliminando i primi due punti si ha una deriva media di 0.0015 m/sec, un dato incredibile considerando che con il solo GPS per ogni secondo la distanza e' di oltre 1 m

La scheda PixHawk contiene all'interno un algoritmo di fusione dei sensori per GPS ed IMU basato su EKF

venerdì 22 marzo 2019

Tre allegri ragazzi morti al Firenze Comics 21 Marzo 2019

Concerto dei Tre Allegri Ragazzi Morti all'interno di Firenze Comics, manifestazione creata dall'Accademia The Sign. Durante il concerto i ragazzi in sala erano invitati a disegnare ispirandosi alla musica (con selezione finale del migliore)


E questo e' come Federico ha visto il concerto (fuori concorso)


Snowden e frigorifero

Parlando con un amico e' saltato fuori il discorso del documentario su Edward Snowden Citizen Four e nel libro No Place to Hide ed il fatto che Snowden richiede ai giornalisti di mettere il cellulare nel forno a micro onde


A questo punto e' saltato fuori....ma funzionera' davvero?? Una fondo di verita' doveva esserci perche' comunque il forno a microonde deve avere una barriera per evitare l'emissione all'esterno di onde elettromagnetiche


E non sono stato il solo ad avere la stessa idea: su Youtube ci sono altre persone che hanno messo il cellulare nel microonde o nel frigorifero. .
I risultati......niente di niente...il cellulare continua a funzionare nel frigorifero e nel microonde

Andando a rileggere in originale il libro la frase originale e' :

Snowden immediately raised issues of security, asking whether I had a cell phone.
My phone only worked in Brazil, but Snowden nonetheless insisted that I remove the battery
or place it in the refrigerator of his minibar, which would at least muffle conversations,
making them more difficult to overhear. 

Quindi se ti ricordi male poi non prendere che le cose funzionino come ti ricordavi

mercoledì 20 marzo 2019

Mkr1010 Wake up da pin e risparmio energetico

Un sistema rapido per mettere in sleep mode una MKR1010 e risvegliarla tramite un interrupt sul pin 8 (che puo' essere simulato connettendo il pin 8 al GND)



-----------------------------------------------------------------
#include "ArduinoLowPower.h"

// Pin per wakeup
const int pin = 8;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pin, INPUT_PULLUP);
  LowPower.attachInterruptWakeup(pin, funzione, CHANGE);
}

void loop() {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
    LowPower.sleep();
}

void funzione {
// qui il codice da eseguire durante l'interrupt
}

Casio FP-200

Un nuovo piccolino si e' aggiunta alla collezione, Casio FP-200 direttamente dal 1982 con processore 80c85, in pratica un concorrente dell'M10

Prima di tutto una nota : attenzione all'alimentazione. Il jack laterale e' un input per un 6V 1A ma con il positivo sull'esterno e non sul pin centrale (c'e' anche una alimentazione con 4 batterie AA nascoste in cassetto sul lato superiore)


La macchina doveva essere funzionante ma all'accensione si presentava immancabilmente questo messaggio Memory illegal con 0 Bytes di memoria libera e premendo Invio gli errori SN Error, FC Error. La documentazione on line e' molto scarsa ma ho trovato il Service Manual ed un User Manual (in tedesco) da cui si legge che in questi casi si deve digitare RESET e premere Invio


 

I banchi di memoria sono sul retro coperti da un coperchio


Schermata di modalita' Basic e CETL (uno spreadsheet integrato)



Da notare la presenza di una porta Centronics


martedì 19 marzo 2019

Arduino MKR1010 ed allarme ricorsivo con RTCZero e risparmio energetico

Aggiornamento: questo post e' molto simile al precedente

Lo scopo di questo script e' generare ogni 10 secondi un allarme che risvegli dall standby la MKR1010 per effettuare una operazione e poi la riaddormenti in staandy


Una volta in Sleep Mode in consumo e' inferiore a 10 mA ovvero e' sotto la soglia di misura del mio strumento

Con questa configurazione il consumo e' di circa 14 mA

Attenzione : quando viene lanciato il programma il processore va in standby e viene disabilitata la porta USB. Per effettuare un nuovo upload si deve premere il tasto di reset per due volte in modo veloce
--------------------------------------------------------------------------
#include <RTCZero.h>

/* Create an rtc object */
RTCZero rtc;

/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 00;
const byte hours = 17;

/* Change these values to set the current initial date */
const byte day = 17;
const byte month = 11;
const byte year = 15;

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  rtc.begin();

  rtc.setTime(hours, minutes, seconds);
  rtc.setDate(day, month, year);

  rtc.setAlarmTime(17, 00, 10);
  // Abilita l'allarme quando i secondi sono uguali alla soglia
  rtc.enableAlarm(rtc.MATCH_SS);
  rtc.attachInterrupt(alarmMatch);

  rtc.standbyMode();
}

void loop()
{
  rtc.standbyMode();    // Sleep until next alarm match
}

void alarmMatch()

  // INVERTE LO STATO DEL LED
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  // Setta il nuovo allarme 10 secondi nel futuro
  rtc.setAlarmSeconds((rtc.getSeconds() + 10) % 60);
}

Complimentary filter con MPU6050 ed Arduino

Per terminare la serie, dopo Kalman e Madgwick un esempio di Complementary Filter
Dal punto di vistra computazionale e' il piu' semplice perche' si tratta di una sola formula
senza ricorsione e matrici

angle = 0.98 *(angle+gyro*dt) + 0.02*acc
(i parametri possono essere ottimizzati)

Una breve statistica (per raffronto con le prove precedenti)

Nr campioni : 20121
Media : -11.97
Range : 0.41
Std : 0.02
Varianza : 0.0005
Skewness :0.62

Il codice e' stato adattato da qui
-----------------------------------------------------------------------
// Complementary filter

#include <Wire.h>

int16_t axRaw, ayRaw, azRaw, gxRaw, gyRaw, gzRaw, temperature;

float pitch=0;
float pitchAcc;
float P_CompCoeff= 0.98;
unsigned long delta_t,old_time;


void ComplementaryFilter(int ax,int ay,int az,int gy,int gz) {
 long squaresum=(long)ay*ay+(long)az*az;
 delta_t = micros()- old_time;
 old_time = micros();

 pitch+=((-gy/32.8f)*(delta_t/1000000.0f)); 
 pitchAcc =atan(ax/sqrt(squaresum))*RAD_TO_DEG;
 pitch =P_CompCoeff*pitch + (1.0f-P_CompCoeff)*pitchAcc;
}


void setup() {
Serial.begin(115200);
  //Inizio configurazione clock interno 
  
  Wire.beginTransmission(0x68);
  Wire.write(0x6B);
  Wire.write(0x00);
  Wire.endTransmission();
  
  // Inizializzazione Accelerometro intervallo di 8g Fattore di scala 4096
  Wire.beginTransmission(0x68);
  Wire.write(0x1C);
  Wire.write(0x10);
  Wire.endTransmission();
  
  // Inizializzazione Gyro Intervallo di 500 deg/sec LSB (fattore di scala) 65.5 deg/s
  Wire.beginTransmission(0x68);
  Wire.write(0x1B);
  Wire.write(0x08);
  Wire.endTransmission();
  old_time = micros();
}

void loop() {
  Wire.beginTransmission(0x68);
  Wire.write(0x3B);
  Wire.endTransmission();
  Wire.requestFrom(0x68, 14);
  while (Wire.available() < 14);
  axRaw = Wire.read() << 8 | Wire.read();
  ayRaw = Wire.read() << 8 | Wire.read();
  azRaw = Wire.read() << 8 | Wire.read();
  temperature = Wire.read() << 8 | Wire.read();
  gxRaw = Wire.read() << 8 | Wire.read();
  gyRaw = Wire.read() << 8 | Wire.read();
  gzRaw = Wire.read() << 8 | Wire.read();

  /*Serial.print(axRaw/4096.0); Serial.print(",");
  Serial.print(ayRaw/4096.0); Serial.print(",");
  Serial.print(azRaw/4096.0); Serial.print(",");
  Serial.print(gxRaw/131.0); Serial.print(",");
  Serial.print(gyRaw/131.0); Serial.print(",");
  Serial.println(gzRaw/131.0);*/
  ComplementaryFilter(axRaw,ayRaw,azRaw,gyRaw,gzRaw);
  Serial.println(pitch);
  delay(2);
}

lunedì 18 marzo 2019

Filtro Madgwick con MPU6050 ed Arduino

Un test del filtro di Madgwick , un possibile sostituto per il filtro Kalman su processori meno potenti

La libreria per Arduino si trova a questo link

Da notare prima di tutte che :

1) la finestra di campionamento del filtro in Madgwick.begin aumenta o diminuisce la velocita' di convergenza dei dati

2) i dati devono essere inseriti nel filtro con le corrette unita' fisiche (m/sec2 e deg/sec) e non con i dati raw in uscita dal sensore

3) la libreria permette di inserire anche i dati di un sensore magnetico (che non e' pero' presente nella 6050)

Una prova veloce con il filtro Madgwick e MPU statica

Nr campioni : 73071
Media : 9.82
Range : 0.56
Std : 0.04
Varianza : 0.001
Skewness : -0.01

Nelle stesse condizioni ma con filtro Kalman

Nr campioni : 16568
Media : 9.84
Range : 0.44
Std : 0.04
Varianza : 0.001
Skewness : 0.05


Aggiornamento : per calcolare il valore della Temperatura in gradi Celsius si usa la formula

T = (lettura/340.0)+36.53

--------------------------------------------------------------------------------------------
#include  <MadgwickAHRS.h> 
#include <Wire.h>

int16_t axRaw, ayRaw, azRaw, gxRaw, gyRaw, gzRaw, temperature;


Madgwick  MadgwickFilter;

void setup() {
Serial.begin(115200);
  //Inizio configurazione clock interno 
  
  Wire.beginTransmission(0x68);
  Wire.write(0x6B);
  Wire.write(0x00);
  Wire.endTransmission();
  
  // Inizializzazione Accelerometro intervallo di 8g Fattore di scala 4096
  Wire.beginTransmission(0x68);
  Wire.write(0x1C);
  Wire.write(0x10);
  Wire.endTransmission();
  
  // Inizializzazione Gyro Intervallo di 500 deg/sec LSB (fattore di scala) 65.5 deg/s
  Wire.beginTransmission(0x68);
  Wire.write(0x1B);
  Wire.write(0x08);
  Wire.endTransmission();

  // Inizializzazione LPF (Filtro passa basso) 43 Hz
  Wire.beginTransmission(0x68);
  Wire.write(0x1A);
  Wire.write(0x03);
  Wire.endTransmission();
  
  MadgwickFilter.begin(100);  // 100 Hz

}

void loop() {
  Wire.beginTransmission(0x68);
  Wire.write(0x3B);
  Wire.endTransmission();
  Wire.requestFrom(0x68, 14);
  while (Wire.available() < 14);
  axRaw = Wire.read() << 8 | Wire.read();
  ayRaw = Wire.read() << 8 | Wire.read();
  azRaw = Wire.read() << 8 | Wire.read();
  temperature = Wire.read() << 8 | Wire.read();
  gxRaw = Wire.read() << 8 | Wire.read();
  gyRaw = Wire.read() << 8 | Wire.read();
  gzRaw = Wire.read() << 8 | Wire.read();

  /*Serial.print(axRaw/4096.0); Serial.print(",");
  Serial.print(ayRaw/4096.0); Serial.print(",");
  Serial.print(azRaw/4096.0); Serial.print(",");
  Serial.print(gxRaw/65.5); Serial.print(",");
  Serial.print(gyRaw/65.5); Serial.print(",");
  Serial.println(gzRaw/65.5);*/
  MadgwickFilter.updateIMU(gxRaw/65.5,gyRaw/65.5,gzRaw/65.5,axRaw/4096.0,ayRaw/4096.0,azRaw/4096.0) ;
  Serial.println(MadgwickFilter.getRoll());
  //ROLL  = MadgwickFilter.getRoll();
  //PITCH = MadgwickFilter.getPitch();
  //YAW   = MadgwickFilter.getYaw();
  delay(2);
}

venerdì 15 marzo 2019

RS485 su Arduino

Dovendo acquisire dati in un posto in cui non posso usare nessuna trasmissione radio e dovendo mettere ad almeno 15-20 m il sensore dal micro controllore cercavo una trasmissione seriale "robusta" ed ho voluto provare la seriale RS485 di tipo industriale che e' data per trasmissioni fno al chilometro (ovviamente dipende dalla velocita' di trasmissione, qualita' dei cavi ecc.)


L'utilizzo e' molto trasparente. Si collega il cavo A+ con tutti i dispositivi presenti sulla linea di comunicazione (possono essere piu' di due)  ed il cavo B-

Il connettore TTL-RS485 deve essere semplicemente alimentato e collegato alla seriale TTL di Arduino sui pin D0 e D1


Sul lato PC il dongle RS485-USB e' stato riconosciuto in modo immediato da Linux

E visto che ci siamo comprati un oscilloscopio perche' non usarlo


Progetto Due

Il progetto due prevede un sistema ad alimentazione solare con una Arduino MKR1400  collegata ad un accelerometro ADXL345 che invia un SMS nel momento in cui il sensore registra un movimento



Per l'alimentazione solare ho usato un modulo basato su CN3065 per la ricarica della LiPo con una LiPo da 2000 mAh ed un pannello solare da 0.5W. I cavi sono stati saldati sulla piastra invece su usare i connettori JST (per la batteria ed il pannello solare ci sono sia i connettori JST che le basi per saldare...per l'output su SYS ho collegato i cavi dietro al JST)


Le connessioni sono piuttosto semplici

ADXL345 : il pin di interrupt e' collegato a  D8 di MKR. Gli altri cavi sono 3.3V, GND SDA ed SCL in modo standard



Un problema che e' stato riscontrato e' che l'interrupt 1 dell'ADXL345 passa da uno stato High ad uno Low mentre la MKR si aspetta l'esatto contrario per attivare l'interrupt
Per questo motivo il collegamento tra INT1 di ADXL345 e D8 di MKR1400 passa attraverso un circuito di logic inverter (in pratica una porta NOT) realizzato con un transistor 2n2222a
Sull'IN e' stato collegato INT1 di ADXL345 e su out D8 di MKR




Circuito di prova...l'interrutore serve a simulare il cambio di stato del pin
Per la versione finale sara' usato l'integrato SN74HC04N, un integrato con 6 porte NOT a tensioni tra 2 e 6V



Il consumo in standby e' di circa 20 mA (mi aspettavo di meno...forse ho sbagliato qualcosa??)




mentre quando viene attivato il GSM e si invia l'SMS la corrente raggiunge un picco di circa 220 mA con valori stabili per una decina di secondi sopra i 100 mA

Questo e' lo sketch di funzionamento
----------------------------------------------------
#include "ArduinoLowPower.h"
#include <SparkFun_ADXL345.h> 
#include <MKRGSM.h>


// Pin per wakeup
const int interruptPin = 8;
// GSM
GSM gsmAccess;
GSM_SMS sms;

ADXL345 adxl = ADXL345();           
volatile int invia = 0;


void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(interruptPin, INPUT);
  delay(5000);
  adxl.powerOn();                     
  adxl.setRangeSetting(2);
  adxl.setInterruptLevelBit(1);
  adxl.setSpiBit(0);                  
  adxl.setActivityXYZ(1, 1, 1);       
  adxl.setActivityThreshold(50);      
  adxl.setTapDetectionOnXYZ(1, 1, 1);
  adxl.setTapThreshold(50);         
  adxl.setTapDuration(55);    
  adxl.ActivityINT(1);
  adxl.singleTapINT(1);
      
  LowPower.attachInterruptWakeup(interruptPin, funzione, CHANGE);
}

void loop() {  

if (invia == 1){
       bool connected = false;
       digitalWrite(LED_BUILTIN, HIGH);
       delay(500);
       digitalWrite(LED_BUILTIN, LOW);
       delay(500);
       while (!connected) {
        if (gsmAccess.begin() == GSM_READY) {
          connected = true;
          } 
       delay(1000);
       sms.beginSMS("+393471xxxxxx");
       sms.print("Luca mi ha toccato");
       sms.endSMS(); 
       invia = 0;  
       }
  }    

LowPower.sleep();
}

void funzione() {
   invia = 1;
}