Detect desktop sway with AtTiny85 and MPU-6050

Un articolo su come rilevare oscillazioni tramite AtTiny85 e MPU6050

In questo articolo illustrerò come leggere il valore dell'accelerazione lungo l'asse X tramite un MPU5060 e un AtTiny85. 

L'obbiettivo è accendere un led se il sensore fissato alla mia scrivania rileva un certo tipo di oscillazioni.

Diverso tempo fa scrissi un dettagliato articolo sul funzionamento dell' Mpu-6050 e un altro su come programmare un AtTiny85.  Questi due articoli sono il punto di partenza.

Nell'articolo sull'MPU6050 ho utilizzato un Arduino e la libreria "Wire" per leggere i dati dal sensore.

Comunicazione I2C

L'AtTiny85 e l'accelerometro comunicano tra di loro via I2C. Per l'AtTiny non è possibile utilizzare la libreria "Wire" ma esiste una libreria che implementa esattemente le stesse funzionalità.

Funzionalità Wire.h TinyWireM.h Note
Inizializzazione Wire.begin() TinyWireM.begin()  
Inizio trasmissione Wire.beginTransmission(MPU) TinyWireM.beginTransmission(MPU) MPU è l'indirizzo del MPU6050 (0x68)
Scrittura Wire.write(0x6B) TinyWireM.send(0x6B) 0x6B è un registro
Lettura Wire.requestFrom(MPU, 6, true);
Wire.read()
TinyWireM.requestFrom(MPU, 6);
TinyWireM.receive()
MPU è l'indirizzo del MPU6050 (0x68)
6 è il numero di byte da leggere
Finetrasmissione Wire.endTransmission(true) TinyWireM.endTransmission(true)  

 

Da punto di vista hardware i collegamenti sono i seguenti:

Mpu-6050 AtTiny85
SCL Pin7
SDA Pin5
INT 6

 

Ecco un esempio di codice per la lettura dei valori di accelerazione sugli assi X e Y:

float AccX, AccY, AccZ;

void setup() {
  TinyWireM.begin();
  TinyWireM.beginTransmission(MPU);       // Inizializzo la comunicazione I2C
  TinyWireM.send(0x6B);                  // Registro 6B
  TinyWireM.send(0x00);                  // setto il registro 6B a 0
  TinyWireM.endTransmission(true);        // fine comunicazione
  
  TinyWireM.beginTransmission(MPU);
  TinyWireM.send(0x1B); // Registro GYRO_CONFIG
  TinyWireM.send(0x10); // Setto il registro a 10 esadecimale, 00010000 binario (1000deg/s full scale)
  TinyWireM.endTransmission(true);

  TinyWireM.beginTransmission(MPU);
  TinyWireM.send(0x1C); // Registro ACCEL_CONFIG
  TinyWireM.send(0x10); // Setto il registro a 10 esadecimale, 00010000 binario (+/- 8g  full scale)
  TinyWireM.endTransmission(true);
}

void readSensorData(){
    TinyWireM.beginTransmission(MPU);
    TinyWireM.send(0x3B); // Parto dal registr 3B (ACCEL_XOUT_H)
    TinyWireM.endTransmission(false);

    TinyWireM.requestFrom(MPU, 6); 
    AccX = TinyWireM.receive() << 8 | TinyWireM.receive(); // asse X
    AccY = TinyWireM.receive() << 8 | TinyWireM.receive(); // asse Y
    AccZ = TinyWireM.receive() << 8 | TinyWireM.receive(); // asse Z
  }

Collegamenti sulla breadboard

Come descritto nella tabella sopra, i pin 5 e 7 dell'AtTiny sono utilizzati per la comunicazione con l' MPU6050.

Il led è connesso al piedino 2 che corrisponde alla portaD3.

Il circuito sulla breadboard è il seguente:

Wiring AtTiny85 to MPU-6050

Il codice per l' AtTiny85

Per l'upload del codice fai riferimento all'articolo Programmare un AtTiny85.

Il codice è il seguente:

#include <TinyWireM.h>

const uint16_t samples = 100;
float vPoints[samples];

const int MPU = 0x68; // Indirizzo I2C MPU6050 
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;


void setup() {
  pinMode(3, OUTPUT);

  digitalWrite(3, HIGH);
  delay(1000);
  digitalWrite(3, LOW);
  delay(1000);
   


  TinyWireM.begin();
  TinyWireM.beginTransmission(MPU);       // Inizializzo la comunicazione I2C
  TinyWireM.send(0x6B);                  // Registro 6B
  TinyWireM.send(0x00);                  // setto il registro 6B a 0
  TinyWireM.endTransmission(true);        // fine comunicazione
  
  TinyWireM.beginTransmission(MPU);
  TinyWireM.send(0x1B); // Registro GYRO_CONFIG
  TinyWireM.send(0x10); // Setto il registro a 10 esadecimale, 00010000 binario (1000deg/s full scale)
  TinyWireM.endTransmission(true);

  TinyWireM.beginTransmission(MPU);
  TinyWireM.send(0x1C); // Registro ACCEL_CONFIG
  TinyWireM.send(0x10); // Setto il registro a 10 esadecimale, 00010000 binario (+/- 8g  full scale)
  TinyWireM.endTransmission(true);
  

  digitalWrite(3, HIGH);
  delay(1000);
  digitalWrite(3, LOW);
  delay(1000);
}

void readSensorData(){

    TinyWireM.beginTransmission(MPU);
    TinyWireM.send(0x3B); // Parto dal registr 3B (ACCEL_XOUT_H)
    TinyWireM.endTransmission(false);

    TinyWireM.requestFrom(MPU, 6); 
    AccX = TinyWireM.receive() << 8 | TinyWireM.receive(); // asse X
    AccY = TinyWireM.receive() << 8 | TinyWireM.receive(); // asse Y
  }

void innerLoop(){
  int counter=0;
  for(int i=0; i<100; i++)
  {
    //leggo n campioni
    for(int i=0; i<samples; i++)
    {
      readSensorData();
      vPoints[i] = AccX;
    }

    float maxVal = vPoints[0];
    float minVal = vPoints[0];
    
    //detemino il minimo, il massimo 
    for (int i = 0; i < samples; i++) {
      if (vPoints[i] > maxVal) {
          maxVal = vPoints[i];
      }
      if (vPoints[i] < minVal) {
          minVal = vPoints[i];
      }
    }

    //se rilevo un'oscillazione > 150 incremento counter
    if(maxVal-minVal>150){
      counter++;
    }

    //se counter super una certa soglia allora significa che il sesore sta oscillando da un po
    if(counter>15)
    {
      for (int i = 0; i < 10; i++) {
        digitalWrite(3, HIGH);
        delay(300);
        digitalWrite(3, LOW);
        delay(300);
      }
      return;
    }
  }
}

void loop() {
  innerLoop();                         
  delay(100);
}

L'idea che sta alla base è:

1) Eseguo X misurazioni

2) Ricavo valori massimi e minimi

4) Se la differenza di Max-Min (che rappresenta l'ampiezza dell'oscillazione) supera una certa soglia incremento una variabile "counter"

3) Se counter è maggiore di una certa soglia faccio lampeggiare il diodo led.