La ruota del criceto Ruby connessa ad Internet

Con una Wemos D1 Mini, un sensore reed e un display, abbiamo monitorato per quasi due anni l’attività notturna del criceto, raccogliendo dati e riflettendo sul tema del benessere animale.

Maggio 31, 2025

Questa è la storia di un progetto di qualche anno fa. Allora in famiglia avevamo ricevuto una cricetina (credo della specie Phodopus campbelli detti anche Blue Hamster), battezzata Ruby, che era tanto carina e gentile finché poi ha iniziato a mordere ed è rimasta solo carina.

Ruby nella sua prima gabbia

Visto che le piaceva tanto la sua ruota e che la ruota del criceto è un po’ la metafora della vita dell’uomo, avevo deciso di ingegnerizzarla per misurare la velocità del roditore e fare qualche statistica, ho pensato poi di tracciare online tutte le sue corse per sapere un po’ di più della vita di Ruby. Eh beh, correva davvero tanto.

Il progetto è del 2021, lo pubblico ora per partecipare al 2025 Pet Hack Contest. 🐹

Fasi del progetto:

  • costruzione del sistema usando una Wemos con connessione ad Internet, un sensore reed e un mini display SSD1306
  • tracciamento dei dati su un Google Sheet e osservazione dei risultati

Idea

Intanto le basi: la velocità è la misura dello spazio nel tempo.

V = S / T

Per misurare la velocità a cui corre il criceto dobbiamo quindi misurare lo spazio percorso e il tempo e poi fare una divisione.

Se sappiamo quanti giri fa la ruota e se conosciamo il raggio della ruota, possiamo calcolare la circonferenza (circonferenza = 2 * pi * r) e quindi possiamo dire quanti metri percorre il criceto.

Misurando anche il tempo in cui corre possiamo quindi fare la divisione S / T e trovare la velocità.

Per contare i giri della ruota usiamo un sensore “reed” che è un sensore che può percepire un campo magnetico, e quindi posizionando opportunamente il sensore e incollando una piccola calamita alla ruota possiamo contare ogni volta che la calamita passa davanti alla ruota.

La posizione del magnete deve essere scelta in modo tale che passi vicino al sensore ad ogni giro.

Facciamo un po’ di prove orientando il sensore in modo da ottenere i risultati migliori, per farlo abbiamo estratto la ruota dalla gabbia, incollato un magnete e incollato il sensore alla gabbia. Contando i giri della ruota e verificando il numero letto dal sensore abbiamo trovato la configurazione migliore.

Problematiche capitate:

  • il magnete può passare vicino al sensore lentamente, e quindi possiamo avere più letture, quindi abbiamo deciso di usare nel codice un confronto con il valore precedente per individuare il gradino di fine passaggio magnete.
  • Come calcolare il tempo? Con i timer. Ogni 2 secondi vediamo quanti giri ha fatto la ruota ed effettuiamo il calcolo per vedere la velocità del criceto in quel momento. In pratica il tempo è sempre 2 secondi. Se il criceto avrà fatto 2 giri (2*pi*6cm = 37cm), vuol dire che ha corso alla velocità di 18.5 cm/s. Quindi la velocità istantanea è calcolata ogni 2 secondi. Ogni due secondi azzeriamo il contatore dei giri.

Con questi calcoli possiamo misurare la velocità del criceto, vedere la velocità massima, misurare lo spazio totale percorso e con un altro timer possiamo tenere traccia anche del tempo di corsa e quindi della velocità media senza tener conto di quando è fermo.

Lo schema del circuito è questo:

Lo schema con Wemos, Display, Sensore

Codice iniziale

Questo è il codice in una versione iniziale (può essere adattata anche ad un Arduino) a cui successivamente ho aggiunto le funzionalità wifi, qui sono già presenti tutte le misure delle grandezze importanti:

/*
 *  Ruby hamster wheel
 */
 
#include "SSD1306.h" 
SSD1306  display(0x3c, D2, D1);

int reedSensor = D7;  // MINI REED SENSOR CONNECTED ON A2

float r;          // wheel radius in cm
float p;          // wheel circumference in m
float vmax = 0;   // max speed reached
float vmed = 0;   // avarage speed

int val = LOW;
int preval = 0;
int c = 0;
int q = 0;
int c0 = 0;
int deltat = 2;  // sampling time in seconds

unsigned long tim=0;
unsigned long tim15=0;

float v,d,dt;

byte flag = 0;

float totS = 0;
unsigned long lastSec=0;
unsigned long debouncetimer = 0;

void setup() {

  // SERIAL MONITOR
  Serial.begin(115200);
  delay(1000);
  Serial.println("start... ");

  // DISPLAY
  display.init();
  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_10);
 
  // REED
  pinMode(reedSensor, INPUT);

  // wheel radius
  r = 6.0;  // cm

  // calculate circunference
  p = 2.0 * (r / 100) * 3.1415;   // meters
  
}

int screen = 0;


void loop() {


  //
  // every 2 (deltat) seconds determine the speed of the hamster
  flag=1;
  if ( millis() > tim ) {
    tim = millis() + deltat * 1000  ;    // milliseconds
    d = p * q;                 // distance run in meters
    v = 3.6 * d  / deltat ;    // speed in km/h calculated every deltat seconds
    q = 0;                     // start back to count rounds every deltat seconds
    if(v>vmax) vmax=v;         // save max speed
    flag=0;                    // data updated
  
  }

  if (v > 0) {    // count the seconds running
    if( millis() > lastSec) {
     lastSec = millis() + 500;
     totS += .5;     // total time running
    }
  }
  
  //
  // Read the reed sensor
  val = digitalRead(reedSensor); 
  
  //
  // detect when signal switch from HIGH to LOW
  //
  //     -----------+
  //                |
  //                +-----------------------
  //
  if (preval == HIGH && val == LOW) {
     
     long unsigned temptime = millis();
     if( temptime - debouncetimer > 200) {
       // prevent double reads for not proper movement of the wheel
       // probably it's better to move the magnet near the ceneter of the wheel
       c++;  // increae total counter 
       q++;  // counter for instant speed and vmax
       debouncetimer = temptime;
     } else {
       val = preval;
     }
  }

  // every 5 seconds switch the screen with different info
  if ( millis() > tim15 ) { 
    tim15 = millis()+5000;
    screen++;
    if(screen >= 4) screen=0;  
  }
  
  // save if read value is 0 or 1
  if (preval != val) preval = val;

  if( c0 != c || flag==0) {
    c0 = c;
    dt = p * c;   // total distance
    if(totS == 0) vmed = 0;
      else vmed = 3.6 * dt / totS ;  // avarage speed (considering only running time)    
  }

  display.clear();

  if(v>0) {
    // it's running!
    
    printLabels(0);
    printInstantSpeed(1);

  } else {
    // it's resting.
    
    printLabels(3);
    printSpin();
    printInstantSpeed(0);
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.setFont(ArialMT_Plain_10);

    // show different info on the "resting screens":
    if (screen == 0) display.drawString(0, 30, String(vmax)+" MAX");  // max speed
    if (screen == 1 && totS > 0) display.drawString(0, 30, String(vmed)+" AVG");   // avarage
    if (screen == 2) display.drawString(0, 30, "Running time: " + String(totS)+"s"); // running time
    if (screen == 3) display.drawString(0, 30, "Distance: " + String(dt)+"m");   // total distance
  }
  display.display(); 
}

void printLabels(int i){
  if(i==2 || i==1 || i==3) {
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.setFont(ArialMT_Plain_10);
    if(i==1 || i==3) display.drawString(0, 0, "km/h");
    display.setTextAlignment(TEXT_ALIGN_RIGHT);
    if(i==2 || i==3) display.drawString(128, 0, "Spins");
  }
  if(i==0){
    display.setTextAlignment(TEXT_ALIGN_CENTER);
    display.setFont(ArialMT_Plain_10);
    display.drawString(64, 0, "km/h");
  }
}

void printSpin(){
  display.setTextAlignment(TEXT_ALIGN_RIGHT);
  display.setFont(ArialMT_Plain_16);
  display.drawString(128, 13, (String)c);  
}

void printInstantSpeed(int big){
  if(big==0) {
    // use small font
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.setFont(ArialMT_Plain_16);
    display.drawString(0, 13, (String)v);
  }
  if(big==1) {
    // use big font
    display.setTextAlignment(TEXT_ALIGN_CENTER);
    display.setFont(ArialMT_Plain_24);
    display.drawString(64, 13, (String)v);
    display.drawString(63, 13, (String)v);
  }  
}

Purtroppo l’unico video che ho trovato nel mio archivio è questo, addirittura antecedente all’introduzione della Wemos, è infatti basato su un Arduino UNO con uno shield col display:

Miglioramenti e connettività IOT

La scelta di utilizzare una scheda Wemos è ovviamente legata alla possibilità di connettersi ad Internet, così ho aggiunto il sistema per effettuare l’invio dei dati online.

Ho utilizzato Google Scripts e un foglio Google Sheet per ricevere i dati. Con un account Google è infatti possibile predisporre degli “endpoint” a cui inviare i dati tramite https. A questi endpoint risponde ovviamente uno script realizzato con gli script di Google (AppScripts). E’ tramite questi scripts che possiamo raggiungere il nostro Google Drive e scrivere in un foglio elettronico.

Il processo per configurare utilizzare Google Drive è descritto su questo Github e spero che sia ancora valido anche se è passato qualche anno.

Alla fine dalla scheda Wemos, connessa ad Internet, possiamo inviare i dati su Google Sheet e guardarli online dal cellulare. Possiamo vedere quando il criceto corre e se è attivo.

Un problema inaspettato nella misurazione delle corse è capitato a causa dell’invio stesso dei dati che avevo previsto ogni mezz’ora. L’invio infatti richiede il collegamento alla rete e al server di Google e queste operazioni possono portar via molti secondi. Se capitano mentre il criceto sta correndo bloccano il conteggio e portano via dei dati che vanno irrimediabilmente persi.

Ho quindi affrontato il problema costruendo una coda di invio che viene velocemente popolata e poi, ogni mezz’ora, solo se Ruby non sta correndo viene inviata al server.

Ho modificato anche lo script di Google per utilizzare due fogli, uno per tenere i totali giornalieri e uno i parziali ogni mezz’ora.

Interfaccia grafica, Access Point, OTA

Ho poi aggiunto una piccola interfaccia (mettendo la Wemos come Access Point e facendo un semplice captive portal) per gestire l’accesso al wifi e salvare SSID e password senza dover riattaccare la Wemos al computer e modificare il codice (utile per esempio se porto la gabbia del criceto in un’altra casa e la Wemos si deve collegare ad un wifi diverso).

Un’altra aggiunta/miglioramento è stata la possibilità di fare l’update del codice OTA (Over The Air), per gestire cioè l’upload del codice nuovo via wifi senza dover collegare fisicamente la Wemos al computer.

Infine ho modificato anche l’interfaccia grafica sul display SSD1306 per mostrare un diagramma lineare per la distanza percorsa.

Conclusioni

L’esperimento è durate 516 giorni, poi Ruby ci ha lasciato e questo è il suo omaggio dei dati alla scienza:

La velocità massima del criceto è 6.73 km/h.

La velocità media del criceto è 1.38 km/h.

Distanza media percorsa in una giornata (24h): 2766 m

Tempo medio di corsa in un giorno: 6955 s (circa 2 ore)

Tutti i dati sono raccolti in questo Google Sheet pubblico che è a disposizioni di chiunque voglia approfondire la vita dei criceti in modo scientifico, il codice sorgente è disponibile nella repository pubblica del progetto su Github, insieme al codice Google Script utilizzato.

Infine ecco qualche grafico e considerazione sulla biologia del criceto!

Con tutti questi dati possiamo scoprire qualcosa della biologia dei criceti, il primo grafico qui sotto mostra la sua attività giornaliera nell’arco di quasi due anni. Si può dire che è un’attività abbastanza discontinua, certe giorni l’animale riposa e non fa quasi nulla, altri giorni ha un’attività frenetica e corre come un pazzo! Un giorno il sistema ha conteggiato addirittura 15 km di strada percorsa, veramente impressionante per un animaletto così piccolo.

E infine se analizziamo i dati relativi alle singole mezz’ore nell’arco di ogni giornata, cioè i dati memorizzati nel foglio “Sheet2” si può vedere che il criceto è attivo per lo più tra le 23.00 e le 7.00 del mattino e questo conferma che è un animale notturno che passa per lo più la giornata dormendo.

Con tutto l’affetto per la piccola Ruby, che per due anni ha fatto parte della nostra vita con la sua presenza silenziosa e i suoi instancabili giri nella ruota, abbiamo deciso di non accogliere più animaletti in casa, soprattutto se destinati a vivere in gabbia.

Anche quando si cerca di fare del proprio meglio – scegliendo una gabbia spaziosa, arredandola con cura, offrendo cibo di qualità e attenzioni quotidiane – resta sempre il dubbio di quanto sia giusto privare un essere vivente della sua libertà naturale. Nessun accessorio, per quanto curato o costoso, potrà mai sostituire la complessità e la ricchezza di un ambiente selvatico, né restituire l’autonomia e l’istinto che ogni animale ha diritto di esprimere.

Questa scelta nasce da un senso di rispetto più profondo: per Ruby, che ci ha insegnato tanto, e per tutti quegli animali che meritano non solo affetto, ma anche dignità e libertà. Non è una rinuncia all’amore per gli animali, anzi, è proprio da lì che nasce la decisione di non tenerli più in cattività.

Author

PHP expert. Wordpress plugin and theme developer. Father, Maker, Arduino and ESP8266 enthusiast.

Comments on “La ruota del criceto Ruby connessa ad Internet”

Lascia un commento

Your email address will not be published. Required fields are marked with an *