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

Bare Metal C64 con Raspberry 3D Case

Sto giocando con BMC64 e, non volendo sacrificare un case di C64 funzionante, mi sono adattato con questo caso stampato in 3D

I colori sono quelli del VIC 20 ma va benissimo cosi'




giovedì 14 marzo 2019

Regolatori di tensione per Arduino

Per provare un pannello solare con una batteria ho preso un regolatore di tensione (il pannello solare esce a circa 19 V mentre la batteria e' da 12 V



 https://www.amazon.it/gp/product/B01EL9IG6K/ref=oh_aui_search_asin_title?ie=UTF8&psc=1
Per prova ho testato un LM317, un regolatore di tensione lineare. Il componente ha una Vdroput di 3V per cui se si applica una tensione di Vin di 12 V non potremo ottenere piu' di 9 V dal Vout

Per pilotare il componente si usano 2 resistenze


Per il calcolo della Vout si usa la formula

R2 = [(Vin/1.25)-1]* R1

Per esempio se si vuole ottenere una Vout di 5v data un R1 di 220 Ohm si dovra' impostare una R2 di 660 Ohm

Questi componenti non sono molto efficienti e dissipano la corrente in eccedenza come calore.
Prendendo una Arduino ed una sorgente di una batteria di 9V si ha che la potenza dissipata e' data da

W = (9-5)V*0.04A = 0.16W

E' necessario usare un dissipatore termico per valori superiori a 0.25W
usando il rapporto di 19°C/W si ha che l'integrato si riscalda di circa 3°C


Regolatore tensione Arduino Uno

Ma ha senso usare un regolatore di tensione esterno su Arduino Uno??
La Arduino Uno usa un regolatore di tensione LDO NCP1117ST50T3G che eroga una tensione fissa di 5V con un dropout di soli 1.2V ed una tensione di Vin di 20 V.
Il pannello solare eroga in piena insolazione 19.7 V.....e' molto al limite forse troppo (le tensioni consigliate per il regolatore di tensione sono 7-12V max 19V) ma in ogni caso e' integrato un circuito di protezione


Feature Matching OpenCv

Il problema e' il seguente: trovare le differenze tra le due foto. Le due immagini sono state riprese a distanza di oltre un anno ed il ...