HamsterTach: DIY hamster wheel tachometer, uploads data to Google Sheets

HamsterTach: Joan the hamster in action having her privacy invaded activity tracked.

Overview: See How They Run

If you’re only interested in how far and fast Joan runs, skip to the bottom…

This is the first tutorial of this length that I’ve written. I welcome feedback (particularly errors spotted) and suggestions, and I’m happy to answer questions in the comments below. Most of all, I’d love to hear from you if you use this post to build your own HamsterTach or something like it.

The code for this project is available below and in a GitHub repository here.

See also: How do Hamsters Outsmart Humans?

HamsterTach use case

If you own a hamster, you’ve probably speculated about how fast and far it runs on its wheel. This project enables you to get hard data on these important questions.

What it does

HamsterTach logs the revolutions of your hamster’s wheel minute-by-minute and uploads the data to a Google Sheet so that you can calculate how fast and far your hamster runs, and get an idea of its habits.

How it works

HamsterTach registers an input (like a button press) each time a magnet attached to the hamster’s wheel passes a magnetic sensor. It counts these inputs, then uses an easy-to-use web interface from IFTTT (“If This Then That”) to upload data to the web. IFTTT connects to a Google Sheet so that you can view the data in (very nearly) real time.

Why HamsterTach?

There are a number of cool hamster wheel tachometer projects online (e.g. here, here, here and here) but none of them offered an easy-to-use data logging feature, so I put together my own. HamsterTach is also quicker and easier to get up and running than the alternatives, is made from a few affordable parts, and requires no soldering* or expertise in electronics.

How to build your DIY hamster tachometer and data logger

Slightly more technical summary

HamsterTach is a tachometer for a hamster wheel made using the commonly used esp32 or esp8266 microcontrollers with built-in WiFi and a A3144 hall-effect (magnetic field) sensor. Data is uploaded to Google Sheets via an IFTTT webhook.

Parts List

  • esp32 or esp8266 (NodeMCU) microcontroller with wifi (see note* below if you want to avoid soldering)
  • Hall Effect Sensor A3144 (adafruit and Sparkfun have different sensors but I imagine they’re basically the same)
  • 1 x LED in whatever colour for an (optional) indicator LED that flashes when the sensor is activated
  • 1 x 10k resistor
  • 1 x 100k resistor (if you use the LED)
  • Mini breadboard
  • Connecting wires (it will help to have three long wires for connecting to the sensor)
  • Small neodium magnet (mine is about 1cm in diameter x 2mm thick)
  • Glue (contact adhesive?) suitable for sticking metal to your hamster wheel
  • Hamster**

1. Assembly

Assemble as shown in this rather sketchy diagram made using TinkerCad (I will use FRITZING next time). Note that the board is an Esp32 / Esp8266 NOT an Arduino. Hall Effect sensor pins: Purple = +ve; Blue = gnd; Grey = data.

After assembling the parts, check the hall effect sensor is working by connecting the esp32 to USB power and bringing a magnet close to the sensor. (The LED should work independently from the code, as long as the esp32 has power).

Each side of the magnet will only activate one side of the sensor, so make a note of which sides work together.

2. Setting up IFTTT maker webhooks

  1. Set up an account on IFTTT.com, then follow these instructions on microcontrollerslab.com for setting up an IFTTT maker webhook to publish sensor data to Google Sheets.
    • Name your webhook Hamster_Tach (or make sure you update the code)
    • Copy the information about your key / the address to post data to to clipboard for use in the code.
    • Note: I found the instructions to get back to the code didn’t work for me, but you can find the address by going to My Applets > Hamster_Tach, then clicking on the small webhooks icon above the description “If Maker Event “Hamster_Tach…”, then clicking Documentation.

3. Edit the code in the Arduino IDE

  1. If you haven’t done so already, install the Arduino IDE, and then install the esp32 library (or esp8266 library) by following these instructions – or if those don’t work try these.
  2. Start a new project and copy the code below – double check for most recent code on Github here.
  3. Edit the code, pasting the details for your WiFi router on lines 31 and 32, and for your IFTTT webhook in place of the Xs on line 34.
  4. Make any adjustments to the timings on line 47.
// Code for Hamster Wheel Tachometer using esp32/ep8266, Hall Effect sensor and uploading data to Googlesheet using IFTTT webhook. 
//Made 1st Aug 2021 by Stu Patience - www.driverlesscrocodile.com - using code for button debounce created 21 Nov 2006  by David A. Mellis / modified 30 Aug 2011 by Limor Fried / modified 28 Dec 2012 by Mike Walters / modified 30 Aug 2016 by Arturo Guadalupiat
// at https://learn.adafruit.com/make-it-switch/debouncing 
// and instructions for IFTTT webhook to upload data to a googlesheet from: https://microcontrollerslab.com/esp32-esp8266-publish-sensor-readings-google-sheets-via-ifttt/
// Feel free to use, adapt and share this code in any way that helps your project
//Notes from IFTTT (copied and pasted for reference):
//To trigger an Event
//Make a POST or GET web request to:
//https://maker.ifttt.com/trigger/Hamster_Tach/with/key/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//With an optional JSON body of:
//{ "value1" : "", "value2" : "", "value3" : "" }
//The data is completely optional, and you can also pass value1, value2, and value3 as query parameters or form variables. This content will be passed on to the action in your Applet.
//You can also try it with curl from a command line.
//curl -X POST https://maker.ifttt.com/trigger/Hamster_Tach/with/key/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Code body:
#ifdef ESP32
  #include <WiFi.h>
#else
  #include <ESP8266WiFi.h>
#endif
#include <Wire.h>
const char* ssid     = "XXXXXXXXX"; // Write your SSID
const char* password = "XXXXXXXXX";  //Write your Password
const char* IFTTT_URL = "/trigger/Hamster_Tach/with/key/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";  // MAKE SURE YOU UPDATE THIS WITH THE CORRECT KEY
const char* server = "maker.ifttt.com";
// constants won't change. They're used here to set pin numbers:
const int buttonPin = 5;   // GPIO 5 > NODEMCU Pin D1 (the for the pushbutton input pin)
// const int ledPin = 4;      // GPIO 4 > NODEMCU Pin D2 (for the LED output pin)
// Variables will change:
int ledState = LOW;         // the current state of the output pin
int buttonState;             // the current reading from the input pin
int lastButtonState = HIGH;  // the previous reading from the input pin
int tachcount = 0;   // sets the variable for the tachometer count
int reportfreq = 60000;  // sets the frequency for reporting count, in milliseconds
// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 1;    // the debounce time in milliseconds; increase if the output flickers DECREASED TO 5 for hall effect switch
unsigned long timer = 0; // variable for the timer
void setup() {
  // initialise buttonPin (for the hall effect sensor)
  
  pinMode(buttonPin, INPUT);
//this was an optional extra output to see if the code was working properly
// pinMode(ledPin, OUTPUT); 
// set initial LED state
//  digitalWrite(ledPin, ledState);
  // intialise Serial output
  Serial.begin(9600);
  delay(5000);
  Serial.println("Intialising Serial Output... \nWelcome to HAMSTER_TACH 2021 - \"See how they run..\" ");
}
void loop() {
  // read the state of the switch into a local variable:
  int reading = !digitalRead(buttonPin);
//  Serial.println(reading);
  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:
  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:
    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;
      // only toggle the LED and count a new rotation if the new button state is HIGH
      if (buttonState == HIGH) {
        ledState = !ledState;
        tachcount += 1;
      }
    }
  }
  // set the LED:
  digitalWrite(ledPin, ledState);
  // save the reading. Next time through the loop, it'll be the lastButtonState:
  lastButtonState = reading;
  
  // trigger the serial report
  
  if (millis() - timer > reportfreq) {
    String string1 = "Tachcount: ";
    
    Serial.println(string1 + tachcount);// print to serial - later this will be uploaded and report
    timer = millis(); // set the timer to the current time
    initWifi();
    makeIFTTTRequest();
    tachcount = 0;
  }
  }
  
void initWifi() {
  Serial.print("Connecting... "); 
  WiFi.begin(ssid, password);  
  int timeout = 10 * 4; // 10 seconds
  while(WiFi.status() != WL_CONNECTED  && (timeout-- > 0)) {
    delay(250);
    Serial.print(".");
  }
  Serial.println("");
  if(WiFi.status() != WL_CONNECTED) {
     Serial.println("Failed to connect, going back to sleep");
  }
  Serial.print("WiFi connected in: "); 
  Serial.print(millis());
  Serial.print(", IP address: "); 
  Serial.println(WiFi.localIP());
}
void makeIFTTTRequest() {
  Serial.print("Connecting to "); 
  Serial.print(server);
  
  WiFiClient client;
  int retries = 5;
  while(!!!client.connect(server, 80) && (retries-- > 0)) {
    Serial.print(".");
  }
  Serial.println();
  if(!!!client.connected()) {
    Serial.println("Failed to connect...");
  }
  
  Serial.print("Request resource: "); 
  Serial.println(IFTTT_URL);
  String jsonObject = String("{\"value1\" : \"") + tachcount + "\"}";
                     
  client.println(String("POST ") + IFTTT_URL + " HTTP/1.1");
  client.println(String("Host: ") + server); 
  client.println("Connection: close\r\nContent-Type: application/json");
  client.print("Content-Length: ");
  client.println(jsonObject.length());
  client.println();
  client.println(jsonObject);
  Serial.println(jsonObject);
        
  int timeout = 5 * 10; // 5 seconds             
  while(!!!client.available() && (timeout-- > 0)){
    delay(100);
  }
  if(!!!client.available()) {
    Serial.println("No response...");
  }
  while(client.available()){
    Serial.write(client.read());
  }
  
  Serial.println("\nClosing Connection");
  client.stop(); 
}

4. Upload the code to your Esp32 / Esp8266

… by pressing upload (you may need to fiddle around to get the right COM port).

5. Test HamsterTach

  1. Once the code has finished uploading, turn on Serial Monitor in the IDE set to 9600 baud to check that your HamsterTach is working.
  2. You should see a welcome message followed by a report every 60 seconds confirming the count (use your magnet to activate the sensor a few times), and confirming that connection to WiFi and the upload to IFTTT has worked.
  3. Log onto Google Sheets and go to the folder you made for the WebHook to check that a Google Sheet has been created containing your data. You should see something like this:

6. Install HamsterTach on your hamster wheel

If you made it this far I’m sure you can work out the rest: work out a spot on the back of your hamster wheel that you can glue the magnet to, then secure the Hall Effect sensor so that it’s close enough to be activated by the magnet each time the wheel rotates.

I put my esp32 into a small plastic box with holes in each end for the power cable and the sensor wires. You might like to tidy the wires to the sensor with tape of some kind or heat-shrink tubing.

7. See How They Run: Analyse your results. Be Amazed.

Joan runs far further than I expected. She averages about 12,000 revolutions per night – about 5km, and her top speed so far is 127rpm or a little over 3.2km/h.

Most people I’ve invited to guess speculated in the region of 1-2,000 revolutions a night, which just goes to show that you need to measure these things.

Onwards and Upwards

Planned improvements:

  1. There might be an issue with the number of IFTTT events allowed per week on a free account. I’ll probably write a modification to the program so that it only uploads non-zero intervals to IFTTT. The numbers are what most of us are interested in, and anyone wanting to create activity graphs can fill the blanks with Python or R.
  2. Python script to summarise the data and perhaps generate graphs.

*To avoid soldering make sure you buy an esp32 or esp8266 with the header pins already attached.
**Yes, and cage, and wheel etc.

I'd love to hear your thoughts and recommended resources...