1 / 10
Hamster Tracker Wheel — zdjęcie 1
1 / 10

Hamster Tracker Wheel

Zamów wydruk tego modelu

Obliczymy ostateczną cenę na podstawie wybranego pliku. Sprawdzisz ją i zaakceptujesz przed płatnością.

Zapisane na tym urządzeniu.

Informacje o modelu

Źródło
Printables
Kategoria
Pets
Licencja
Creative Commons — Attribution — Noncommercial — Share Alike

Ever wondered how far your furry friend runs while you sleep?
Hi, I'm Loek, 14 years old, and proud owner of the world's cutest hamster! I've always been curious about how active my hamster really is, especially during those night hours when I'm asleep and she is most active.

This curiosity led me to create the Hamster Tracker Wheel. A project that perfectly combines my love for my pet with my passion for electronics and the 3D printer I bought last year. Over the past weeks, I've designed, programmed, printed, and tested this device that measures how far my hamster runs each day, and night.


How It Works
The tracking system utilizes two magnets embedded in the wheel itself. A Hall effect sensor mounted on the stand detects each time a magnet passes by, sending a signal to an Arduino Uno microcontroller. After every two detection's (representing one complete rotation), the Arduino calculates the distance traveled based on the wheel's diameter and updates the display, complete with a tiny running hamster animation!


Safety First
I've designed this with my hamster's safety in mind. All wires are carefully positioned so they remain approximately two centimeters outside the cage's ribs, completely out of reach for hamster paws or teeth.


I hope other people will enjoy using or even expanding upon my Hamster Tracker Wheel project to monitor their own hamsters or other small pets. If you have any questions or reaction, I love to hear them!
 

Tools Used:

  • 3D Modeling: Tinkercad was used to design all the 3D printable parts.
  • Programming: The Arduino code was written using the Arduino IDE, with some assistance from AI for specific functions.

Printing Instructions:

  • PRINT USING PLA! PLA is generally considered the safest and non-toxic filament option for items potentially chewed by pets like hamsters.
  • Print with a layer height of 0.20mm or lower for best results.
  • All parts are designed to be printed without support material.

 Assembly instructions

  1. Insert the 2 magnets into the designated slots on the wheel. Use a small amount of glue (like superglue) to secure them firmly.
  2. Connect the Arduino Uno and the I2C LCD screen according to the wiring diagram, but do not connect the Hall effect sensor wires to the sensor itself yet.
  3. Place the connected Arduino Uno and LCD screen inside the Arduino_Uno_case. Carefully feed the wires intended for the Hall effect sensor through the designated hole in the case.
  4. Place the lid (Arduino_Uno_case_lid) onto the Arduino case.
  5. Mount the printed wheel onto the wheel_stand. Ensure it spins freely.
  6. Slide the hall_effect_sensor_box onto the top mounting point of the wheel_stand.
  7. Feed the Hall effect sensor wires (coming from the Arduino case) through the hole in the hall_effect_sensor_box.
  8. Connect these wires to the Hall effect sensor pins.
  9. Carefully place the Hall effect sensor inside its box, ensuring it's positioned correctly to detect the magnets as the wheel spins. Secure the lid (hall_effect_sensor_box_lid).
  10. Slide the hook into the slot on the Arduino_Uno_case.
  11. Connect the Arduino to your computer via USB and upload the provided code using the Arduino IDE.
  12. Hang the assembled tracker onto your hamster's cage using the hook.
  13. Power the Arduino (via USB or a separate power supply). Assembly is complete!

Wiring diagram

Printed parts you need

  • Arduino_Uno_case_lid
  • hook
  • Arduino_Uno_case
  • wheel_stand
  • hall_effect_sensor_box_lid
  • hall_effect_sensor_box
  • wheel

None printed parts you need

 

PartQuantitySpecification (Example)
Arduino Uno1R3
I2C LCD Screen11602 (16 chars, 2 lines)
Magnetic hall effect sensor1KY-003 module or similar
Magnets2Neodymium, e.g., 5x5x5 mm
GlueSmallSuitable for PLA and magnets
USB Cable1Type A to B (for programming)
Power Supply (Optional)1e.g., 9V adapter or battery pack

Code

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

// --- Function Declarations --- void updateDisplay(); // Declare the update function

// Define the custom hamster character frames (indices 0-3) byte hamsterFrame0[8] = { B01010, B01110, B10101, B11111, B11111, B11111, B01110, B01010 }; // Feet 1 byte hamsterFrame1[8] = { B01010, B01110, B10101, B11111, B11111, B11111, B01110, B00010 }; // Feet 2 byte hamsterFrame2[8] = { B01010, B01110, B10101, B11111, B11111, B11111, B01110, B01010 }; // Feet 3 (same as 1) byte hamsterFrame3[8] = { B01010, B01110, B10101, B11111, B11111, B11111, B01110, B01000 }; // Feet 4

const int hallSensorPin = 7; const float wheelDiameterCM = 11.1; // <<< Adjust wheel diameter here (in cm) float counter = -0.5; // Use float for half rotations bool magnetDetected = false; unsigned long lastDebugTime = 0; float distanceCM = 0.0; // Variable to store distance in cm

// Animation variables int currentHamsterFrame = 0; unsigned long lastHamsterFrameTime = 0; const int hamsterFrameDelay = 250; // Milliseconds between frames

// Helper function to count digits in the integer part int countIntDigits(long n) { if (n == 0) return 1; int count = 0; unsigned long long num = (n < 0) ? -n : n; // Handle potential negative, though not expected here while (num > 0) { num /= 10; count++; } return count; }

void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); pinMode(hallSensorPin, INPUT_PULLUP);

// Load the custom character frames into LCD memory slots 0-3 lcd.createChar(0, hamsterFrame0); lcd.createChar(1, hamsterFrame1); lcd.createChar(2, hamsterFrame2); lcd.createChar(3, hamsterFrame3);

// Initial display setup updateDisplay(); // Call the update function to draw initial screen

Serial.println("System initialized. Enter a number to set rotations."); }

void loop() { unsigned long currentTime = millis(); // Get current time once per loop

// --- Serial Input Handling --- if (Serial.available() > 0) { String input = Serial.readStringUntil('\n'); input.trim(); // Remove leading/trailing whitespace

// Attempt to convert string to integer (then potentially float)
long newCountInput = input.toInt(); // Read as long first for validation

// Check if the input was likely a valid number (non-negative)
if (input.length() &gt; 0 &amp;&amp; newCountInput &gt;= 0) {
    counter = (float)newCountInput; // Update counter, cast to float
    distanceCM = counter * wheelDiameterCM * PI; // Recalculate distance
    updateDisplay(); // Update the LCD immediately
    Serial.print("Counter set to: ");
    Serial.println(counter, 1); // Print float with 1 decimal place
} else {
    Serial.println("Invalid input. Please enter a non-negative whole number.");
}
// Clear any remaining serial buffer data
while(Serial.available() &gt; 0) {
  Serial.read();
}

}

// --- Hamster Animation --- if (currentTime - lastHamsterFrameTime >= hamsterFrameDelay) { lastHamsterFrameTime = currentTime; currentHamsterFrame = (currentHamsterFrame + 1) % 4; // Cycle through frames 0, 1, 2, 3 lcd.setCursor(15, 0); // Position cursor for hamster lcd.write((byte)currentHamsterFrame); // Display the current frame }

// --- Sensor Reading and Logic --- int sensorState = digitalRead(hallSensorPin);

// Debug output every second if (currentTime - lastDebugTime >= 1000) { Serial.print("Sensor State: "); Serial.println(sensorState); lastDebugTime = currentTime; // Use currentTime already obtained }

// Als magneet gedetecteerd wordt (LOW) en we waren niet al een magneet aan het detecteren if (sensorState == LOW && !magnetDetected) { counter += 0.5; // Increment by 0.5 for half rotations magnetDetected = true;

// Calculate distance
distanceCM = counter * wheelDiameterCM * PI; // Use float counter directly

Serial.print("Rotation detected! Count: ");
Serial.print(counter, 1); // Print float with 1 decimal place
Serial.print(", Dist (cm): ");
Serial.println(distanceCM);

// Update the display using the function
updateDisplay();

} // Als geen magneet meer gedetecteerd wordt, reset de detectie status else if (sensorState == HIGH) { magnetDetected = false; } }

// --- Function Definitions ---

// Function to update both lines of the LCD void updateDisplay() { // Update LCD Line 1: Rotations (Hamster is updated separately by animation logic) lcd.setCursor(0, 0); lcd.print("Rotations: "); // Clear previous number lcd.setCursor(11, 0); lcd.print(counter, 1); // Print float counter with 1 decimal place // Hamster position (15, 0) is handled by the animation logic in loop() lcd.setCursor(15, 0); lcd.write((byte)currentHamsterFrame);

// Update LCD Line 2: Distance with dynamic unit switching and precision
lcd.setCursor(0, 1);
lcd.print("Dist:           "); // Clear row
lcd.setCursor(6, 1);        // Position cursor after "Dist: "
int maxNumChars = 8;        // Max chars available for the number part (value + '.' + decimals)

if (distanceCM &lt; 10000.0) { // Display in cm (&lt; 100m)
    lcd.print(distanceCM, 2);
    lcd.print(" cm");
} else if (distanceCM &lt; 10000000.0) { // Display in m (&lt; 100km)
    float distanceM = distanceCM / 100.0;
    long intPartM = (long)distanceM;
    int intDigitsM = countIntDigits(intPartM);
    int decimalPlacesM = max(0, maxNumChars - intDigitsM - 1);
    lcd.print(distanceM, decimalPlacesM);
    lcd.print(" m");
} else { // Display in km (&gt;= 100km)
    float distanceKM = distanceCM / 100000.0;
    long intPartKM = (long)distanceKM;
    int intDigitsKM = countIntDigits(intPartKM);
    int decimalPlacesKM = max(0, maxNumChars - intDigitsKM - 1);
    lcd.print(distanceKM, decimalPlacesKM);
    lcd.print(" km");
}

}

Code Explanation

  • Library: Use the LiquidCrystal_I2C library by Frank de Brabander. Install it via the Arduino IDE Library Manager (Sketch > Include Library > Manage Libraries...).
  • Functionality: The code displays the following on the LCD:
    • Line 1: The total number of full rotations (with one decimal place) and an animated running hamster character.
    • Line 2: The total distance run, automatically switching units between centimeters (cm), meters (m), and kilometers (km) for better readability based on the distance.
  • Customization:
    • You can customize the running hamster animation!
      • Lines 10-13 in the code (byte hamsterFrame0[8] to byte hamsterFrame3[8]) define the four animation frames.
      • Each byte array represents one custom character, displayed as a 5x8 pixel grid on the LCD.
      • Inside the {} for each frame, the Bxxxxx values define the pixel rows (5 pixels wide). 1 means the pixel is ON, 0 means it's OFF.
      • Tip: You can easily design your own 5x8 pixel characters using online 'LCD Custom Character Generator' tools (for example: https://chareditor.com). Then, simply copy the generated byte arrays and replace the existing hamsterFrame data in the code
    • Verify the I2C address of your LCD screen (set in the LiquidCrystal_I2C lcd(...) line). Common addresses are 0x27 or 0x3F. You can use an "I2C Scanner" sketch (easily found online) to find your screen's address if it doesn't work initially.
  • Counting Logic:
    •  The code increments the counter variable by 0.5 each time a magnet is detected (which corresponds to a half-rotation).  Therefore, after one full rotation (two magnet detections), the counter increases by 1.0. This means the counter variable directly stores the total number of full rotations.  The distance is calculated correctly using this full rotations counter multiplied by the full wheel circumference (distanceCM = counter * wheelDiameterCM * PI).  The number of rotations shown on the LCD (Rotations: …) is the value of counter printed directly, representing the total full rotations.

Credits Arduino Uno case: Vafi

Tagi