Detect desktop sway with AtTiny85 and MPU-6050
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:

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.