FINAL PROJECT
FITNESSCAT
Smart gym for cats built around a wireless snack dispenser that provides instant rewards for physical activity. It connects to various exercise devices that use cat-friendly lights and sounds to signal progress without disturbing humans.
Wireless Cat Paw Button – Placed at a distance to encourage movement; pressing it triggers a snack from the main dispenser.
Treadmill Wheel – Tracks distance and rewards the cat once a preset goal is reached.
Future Expansions – Additional devices like a Climbing Tower or Wall (rewarded upon reaching the top) and a Cat Communicator that lets felines leave simple "messages" for their owners.
FnessCat promotes healthy exercise and fun interaction for indoor cats.
✅SLIDE & PRESENTATION
••••••••••••••••••••••••••••••
FINAL PRODUCTS
✅CAT’S PAW BUTTON
FILES
STL
✅REWARD MACHINE
FILES
STL
✅TREADMILL
FILES
STL
••••••••••••••••••••••••••••••
FINAL PROGRAMMING
✅CAT’S PAW BUTTON
PUSH BUTTON + DEBUG LED + 33 NEOPIXEL LED STRIP + EXTERNAL BUTTON
push and external buttons activate the debug led and neopixel 33 leds strip (all yellow)
platformio.ini
[env:seeed_xiao_esp32c3]
platform = espressif32
board = seeed_xiao_esp32c3
framework = arduino
monitor_speed = 115200
upload_speed = 460800
lib_deps = Adafruit NeoPixel
main.cpp
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
// Pin definitions
#define BUTTON1_PIN D7 // Internal button
#define BUTTON2_PIN D8 // External button
#define LED_PIN D2 // Debug LED
#define NEOPIXEL_PIN D1 // NeoPixel data line
#define NUM_PIXELS 33 // Number of LEDs in the NeoPixel strip
// Create NeoPixel object
Adafruit_NeoPixel strip(NUM_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
void setup() {
// Set buttons as input
pinMode(BUTTON1_PIN, INPUT);
pinMode(BUTTON2_PIN, INPUT);
// Debug LED as output
pinMode(LED_PIN, OUTPUT);
// Initialize the NeoPixel strip
strip.begin();
strip.show(); // Turn all LEDs off
}
void loop() {
// Read button states
int button1State = digitalRead(BUTTON1_PIN);
int button2State = digitalRead(BUTTON2_PIN);
// Check if either button is pressed (LOW = pressed, due to pull-up logic)
bool pressed = (button1State == LOW || button2State == LOW);
// Turn debug LED on or off
digitalWrite(LED_PIN, pressed ? HIGH : LOW);
// If pressed, turn NeoPixels yellow; else, turn them off
if (pressed) {
for (int i = 0; i < NUM_PIXELS; i++) {
strip.setPixelColor(i, strip.Color(255, 255, 0)); // Yellow
}
} else {
for (int i = 0; i < NUM_PIXELS; i++) {
strip.setPixelColor(i, 0); // Off
}
}
strip.show(); // Update the strip with new values
delay(10); // Small delay to stabilize loop
}
✅REWARD MACHINE
PUSH BUTTON + DEBUG LED + 14 NEOPIXEL LED STRIP + BUZZER + STEPPER MOTOR + EXTERNAL BUTTON
push and external buttons activate the debug led and neopixel 15 leds strip together with sound, when the last led go on, the stepper motor rotates 1 rotation to the right
platformio.ini
[env:seeed_xiao_esp32c3]
platform = espressif32
board = seeed_xiao_esp32c3
framework = arduino
monitor_speed = 115200
upload_speed = 460800
lib_deps = AccelStepper, Adafruit NeoPixel
main.cpp
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include <AccelStepper.h>
// Define pins
#define BUTTON_PIN1 D7 // Original push button
#define BUTTON_PIN2 D8 // External push button
#define LED_PIN D0 // Debugging LED
#define SPEAKER_PIN D9 // Buzzer
// LED strip pins & LED number
#define LED_STRIP_PIN D10 // Neopixel LED strip
#define NUM_LEDS 14 // Total LEDs
// Stepper motor pins (ULN2003 with 28BYJ-48)
#define IN1 D5
#define IN2 D6
#define IN3 D2
#define IN4 D1
// NeoPixel setup
Adafruit_NeoPixel strip(NUM_LEDS, LED_STRIP_PIN, NEO_GRB + NEO_KHZ800);
// Stepper setup
AccelStepper stepper(AccelStepper::HALF4WIRE, IN1, IN3, IN2, IN4);
// Forward declarations
void lightUpStripWithSound();
void rotateStepper();
void setup() {
Serial.begin(115200);
delay(500); // Give time for Serial to connect
Serial.println("=== Setup started ===");
pinMode(LED_PIN, OUTPUT);
pinMode(SPEAKER_PIN, OUTPUT);
pinMode(BUTTON_PIN1, INPUT_PULLUP); // Use internal pull-up
pinMode(BUTTON_PIN2, INPUT_PULLUP); // Use internal pull-up for new button
strip.begin();
strip.show();
stepper.setMaxSpeed(1000.0);
stepper.setAcceleration(800.0);
Serial.println("=== Setup complete ===");
}
void loop() {
// Check if either button is pressed
if (digitalRead(BUTTON_PIN1) == LOW || digitalRead(BUTTON_PIN2) == LOW) {
Serial.println("=== Button pressed! Activating sequence ===");
digitalWrite(LED_PIN, HIGH);
lightUpStripWithSound();
strip.clear();
strip.show();
rotateStepper();
digitalWrite(LED_PIN, LOW);
Serial.println("=== Sequence complete ===\n");
// Debounce delay
delay(500);
}
}
void lightUpStripWithSound() {
Serial.println("[LIGHT + SOUND] Sequence started");
for (int i = 0; i < NUM_LEDS; i++) {
if (i < NUM_LEDS - 1) {
strip.setPixelColor(i, strip.Color(0, 0, 255)); // Blue
} else {
strip.setPixelColor(i, strip.Color(255, 0, 0)); // Red
}
int freq = map(i, 0, NUM_LEDS - 1, 500, 2000); // Forward frequency
tone(SPEAKER_PIN, freq, 100);
strip.show();
Serial.printf("[LED] LED #%d ON | [Tone] %d Hz\n", i + 1, freq);
delay(100);
}
tone(SPEAKER_PIN, 2500, 300);
delay(300);
noTone(SPEAKER_PIN);
Serial.println("[LIGHT + SOUND] Sequence complete");
}
void rotateStepper() {
int stepsPerRevolution = 2048;
Serial.println("[STEPPER] Rotating 1 full turn CW");
stepper.move(stepsPerRevolution); // 1 full turn CW
while (stepper.distanceToGo() != 0) {
stepper.run();
}
Serial.println("[STEPPER] Rotation complete");
}
✅TREADMILL
PUSH BUTTON + DEBUG LED + HALL SENSOR + 1 NEOPIXEL LED
platformio.ini
[env:seeed_xiao_esp32c3]
platform = espressif32
board = seeed_xiao_esp32c3
framework = arduino
monitor_speed = 115200
upload_speed = 460800
lib_deps = Adafruit NeoPixel
main.cpp
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
// Pin definitions
#define DEBUG_LED_PIN D0
#define NEOPIXEL_PIN D1
#define HALL_SENSOR_PIN D8
#define BUTTON_PIN D7
// NeoPixel setup
#define NUM_PIXELS 1
Adafruit_NeoPixel pixels(NUM_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
// State tracking
unsigned long impulseCount = 0;
int lastSensorState = HIGH;
int lastButtonState = HIGH;
void setup() {
pinMode(HALL_SENSOR_PIN, INPUT);
pinMode(BUTTON_PIN, INPUT);
pinMode(DEBUG_LED_PIN, OUTPUT);
pixels.begin(); // Initialize NeoPixel
pixels.clear();
pixels.show(); // Ensure off at start
Serial.begin(115200);
}
void setNeoPixelColor(uint8_t r, uint8_t g, uint8_t b) {
pixels.setPixelColor(0, pixels.Color(r, g, b));
pixels.show();
}
void loop() {
int sensorState = digitalRead(HALL_SENSOR_PIN);
int buttonState = digitalRead(BUTTON_PIN);
// Activation logic
bool activated = (sensorState == LOW || buttonState == LOW);
if (activated) {
digitalWrite(DEBUG_LED_PIN, HIGH);
setNeoPixelColor(0, 0, 255); // Blue
} else if (impulseCount % 20 == 0 && impulseCount != 0) {
setNeoPixelColor(255, 0, 0); // Red every 20 impulses
digitalWrite(DEBUG_LED_PIN, LOW);
} else {
digitalWrite(DEBUG_LED_PIN, LOW);
setNeoPixelColor(0, 0, 0); // Off
}
// Impulse counting on falling edge
if (lastSensorState == HIGH && sensorState == LOW) {
impulseCount++;
Serial.print("Impulse Count: ");
Serial.println(impulseCount);
}
lastSensorState = sensorState;
// Button logging on falling edge
if (lastButtonState == HIGH && buttonState == LOW) {
Serial.println("Button Pressed");
}
lastButtonState = buttonState;
delay(10); // Debounce
}
••••••••••••••••••••••••••••••
FINAL PCB’s
✅CAT’S PAW BUTTON
FILES
KICAD
✅REWARD MACHINE
FILES
KICAD
✅TREADMILL
FILES
KICAD
••••••••••••••••••••••••••••••
17. INVENTION & INTELECTUAL PROPERTY (WEEK19)
To protect my work while still allowing others to reuse it non-commercially, I chose the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License (CC BY-NC-SA 4.0).
According to this license:
- You are free to:
- Share — copy and redistribute the material in any medium or format
- Adapt — remix, transform, and build upon the material
- Under the following terms:
- Attribution — You must give appropriate credit.
- NonCommercial — You may not use the material for commercial purposes.
- ShareAlike — You must distribute your contributions under the same license as the original.
This approach reflects the educational and collaborative purpose of the project while preserving the option to license it differently in the future if needed.
REWARD MACHINE PROTOTYPE UPDATE
TREADMILL PROTOTYPE UPDATE
The new side panels
Mechanical system for the hall sensor with PCB box holder
I also changed the possition of the cone rollers to the inside for more stability
16. PROJECT DEVELOPMENT UPDATE (WEEK18)
CAT’S PAW BUTTON
Final 3D model, wit a new bottom lid, and updated base structure with a bigger slot for the cable and PCB board
Wiring, PCB mount and button mechanic system
There was lots of prototyping of the button itself, that was keep braking. Here’s the final section through the button system
Final prints:
Bottom lid has some ant slippery pads
FINAL PRODUCT
15. SYSTEM INTEGRATION (WEEK16)
CAT’S PAW BUTTON FINAL PCB
CAT’S PAW BUTTON 3D MODEL & PROTOTYPING UPDATE
TREADMIL 3D MODEL & PROTOTYPING UPDATE
14. INTERFACE AND APLICATION PROGRAMMING (WEEK15)
TREADMILL PCB + PROCESSING CODE
PROCESSING CODE
import processing.serial.*; // Import the Serial library to communicate with Arduino
Serial myPort; // Declare a Serial object for communication
PImage img; // Declare an image object to hold the .png image
float angle = 0; // Angle of rotation for the image
boolean rotateImage = false; // Boolean flag to track whether image should rotate
void setup() {
size(800, 800); // Set the size of the display window to 800x800 pixels
img = loadImage("cat.png"); // Load the image file (must be in sketch folder)
img.resize(200, 200); // Resize the image to 200x200 pixels
println(Serial.list()); // Print a list of available serial ports to the console
// Choose the correct serial port from the list (index [2] in this case)
// This should match the port your Arduino/XIAO is connected to
String portName = Serial.list()[2];
myPort = new Serial(this, portName, 115200); // Open the serial port at 115200 baud
myPort.bufferUntil('\n'); // Tell Processing to read incoming data until it sees a newline '\n'
}
void draw() {
background(255); // Clear the background with white color every frame
pushMatrix(); // Save the current transformation matrix
translate(width/2, height/2); // Move the origin (0,0) to the center of the screen
if (rotateImage) { // If the flag is true (sensor/button is active)
angle += 0.05; // Increase the rotation angle a bit (controls speed of rotation)
}
rotate(angle); // Apply the rotation transformation
imageMode(CENTER); // Draw the image centered on the origin
image(img, 0, 0); // Draw the image at the rotated origin (screen center)
popMatrix(); // Restore the original transformation matrix
}
void serialEvent(Serial myPort) {
// This function is automatically called when new serial data is available
String inData = trim(myPort.readStringUntil('\n')); // Read incoming serial data up to newline and trim spaces
println("Received: " + inData); // Print the received message to the console
// If Arduino sent "LED ON", start rotating the image
if (inData.equals("LED ON")) {
rotateImage = true;
}
// If Arduino sent "LED OFF", stop rotating the image
else if (inData.equals("LED OFF")) {
rotateImage = false;
}
}
13. MOLDIG & CASTING (WEEK14)
CAT’S PAW BUTTON 3D MODEL UPDATE & SILICON CAST
12. MACHINE BUILDING (WEEK12/13)
****I build a plotter that could be an extension to my FinessCat
11. NETWORKING & COMMUNICATION (WEEK11)
SYSTEM SCHEMATIC
CAT’S PAW BUTTON NEW PCB BOARD
Unfortunately I overlooked a mistake I did in THE REWARD MACHINE’s PCB and I repeated it:
I spend 2 days looking for the problem in the code and in the program (because the wiring and paths were showing good connection, but between wrong pins on two boards)
I struggled with the production software and my PCB came out with very thin paths, since I couldn’t put the offset OUT.
But I mange to solder everything.
Some of the paths unglued from the board, because they were just a little bit thicker then hair (on the photo they look nicer then in reality) and the pin header didn’t have enough surface to hold onto so while I was detaching the Xiao to make some jumping wires connection because of the wrong pinouts- it pulled everything out
But I need to make a new PCB anyways…
I2C CONNECTION BETWEEN THE CAT’S PAW BUTTON & THE REWARD MACHINE CODES
Since I destroyed THE CAT’S PAW BUTTON PCB I used a breadboard, and THE REWARD MACHINE PCB had possibility to use I2C connection because on my pin D6 I also had a header.
I switched to VISUAL STUDIO CODE & PlatformIO platformio.ini
[env:seeed_xiao_esp32c3]
platform = espressif32
board = seeed_xiao_esp32c3
framework = arduino
monitor_speed = 115200
upload_speed = 460800
REWARD MACHINE square board - MAIN BOARD
main.cpp
#include <Arduino.h>
#include <Wire.h>
const int button = D7; // Button pin
const int led = D2; // Local LED (optional)
const int squareAddress = 0x08; // I2C address of Square
bool lastButtonState = HIGH; // Previous state (starts HIGH = not pressed)
void setup() {
delay(1000);
Serial.begin(115200);
pinMode(button, INPUT); // External pull-up
pinMode(led, OUTPUT); // Optional local LED
Wire.begin(); // Start I2C as master
Serial.println("Circle ready (I2C master)");
}
void loop() {
bool currentState = digitalRead(button);
// Only act on state change
if (currentState != lastButtonState) {
lastButtonState = currentState;
// Invert to get "pressed = LOW"
bool pressed = (currentState == LOW);
digitalWrite(led, pressed); // Optional: reflect button locally
Wire.beginTransmission(squareAddress); // Begin I2C transmission
Wire.write(pressed ? '1' : '0'); // Send '1' or '0'
Wire.endTransmission(); // End transmission
Serial.println(pressed ? "Button pressed → LED ON" : "Button released → LED OFF");
}
delay(50); // Small debounce delay
}
CAT’S PAW BUTTON circle board SECONDARY BOARD
- changed for the a breadboard -
main.cpp
#include <Arduino.h>
#include <Wire.h>
const int led = D7; // LED pin
const int i2cAddress = 0x08; // I2C address of this board
// Function to handle incoming I2C data
void receiveEvent(int numBytes) {
if (numBytes > 0) {
char command = Wire.read(); // Read the first byte
if (command == '1') {
digitalWrite(led, HIGH); // Turn LED on
Serial.println("LED ON (received 1)");
} else if (command == '0') {
digitalWrite(led, LOW); // Turn LED off
Serial.println("LED OFF (received 0)");
}
}
}
void setup() {
delay(1000);
Serial.begin(115200);
pinMode(led, OUTPUT); // Set LED as output
digitalWrite(led, LOW); // Ensure LED is off initially
Wire.begin(i2cAddress); // Start I2C as slave
Wire.onReceive(receiveEvent); // Register receive event handler
Serial.println("Square ready (I2C slave)");
}
void loop() {
// Nothing needed here — everything happens on receiveEvent
}
10. OUTPUT DEVICES (WEEK10)
REWARD MACHINE CODE
After pressing the button:
- the debug led should go ON only when the button is pressed, and OFF when the button is realised
- the led strip increasing in blue colour from led 1 -37 and last one 38th is red
- the buzzer increases the sound together with the led strip, makes a stronge sound on the last, 38th red led
- the servo motor rotates after led strip sequence is finished
PUSH BUTTON + LED + SERVO + NEOLED (ARDUINO IDE)
#include <Adafruit_NeoPixel.h>
#include <ESP32Servo.h>
#define BUTTON_PIN D7 // Button with pull-up resistor on D7
#define LED_PIN D2 // Debugging LED on D2
#define LED_STRIP_PIN D9 // Neopixel LED strip on D9
#define SPEAKER_PIN D6 // Buzzer on D6
#define SERVO_PIN D8 // Servo motor on D8
#define NUM_LEDS 38 // Total LEDs in strip
Adafruit_NeoPixel strip(NUM_LEDS, LED_STRIP_PIN, NEO_GRB + NEO_KHZ800);
Servo servoMotor;
void setup() {
pinMode(BUTTON_PIN, INPUT); // Set button pin as input
pinMode(LED_PIN, OUTPUT); // Set LED pin as output
pinMode(SPEAKER_PIN, OUTPUT); // Set buzzer pin as output
strip.begin();
strip.show(); // Initialize all pixels to 'off'
servoMotor.attach(SERVO_PIN);
servoMotor.write(90); // Stop position for continuous servo
}
void loop() {
int buttonState = digitalRead(BUTTON_PIN); // Read button state
if (buttonState == LOW) { // Button is active low due to pull-up resistor
digitalWrite(LED_PIN, HIGH); // Turn on debug LED when button is pressed
lightUpStripWithSound();
strip.clear(); // Turn off all LEDs after animation
strip.show();
rotateServo(); // Start servo movement after LED sequence finishes
} else {
digitalWrite(LED_PIN, LOW); // Turn off debug LED when button is released
}
}
void lightUpStripWithSound() {
for (int i = 0; i < NUM_LEDS; i++) {
if (i < NUM_LEDS - 1) {
strip.setPixelColor(i, strip.Color(0, 0, 255)); // LED blue for 1 to 37
} else {
strip.setPixelColor(i, strip.Color(255, 0, 0)); // Last LED (38) red
}
tone(SPEAKER_PIN, map(i, 0, NUM_LEDS - 1, 500, 2000), 100); // Increasing "ping"
strip.show();
delay(100); // Delay for effect
}
tone(SPEAKER_PIN, 2500, 300); // Final stronger ping
delay(300);
noTone(SPEAKER_PIN); // Stop buzzer
}
void rotateServo() {
for (int i = 0; i < 2; i++) { // Move 0 → 180 → 0 two times
servoMotor.write(180);
delay(500); // Allow time to reach position
servoMotor.write(0);
delay(500); // Allow time to return
}
}
Unfortunately I’m not able to write the code correctly. The debug led is ON during the whole time function is running (when the button is not pressed anymore) and the servo is going crazy.
I’m not understanding the problem because the code below works fine
SERVO MOVES FROM 0 → 180 → 0 TWICE AFTER PRESSING THE BUTTON (ARDUINO IDE)
#include <ESP32Servo.h>
#define BUTTON_PIN D7 // Button with pull-up resistor on D7
#define SERVO_PIN D8 // Servo motor on D8
Servo servoMotor;
bool buttonPressed = false;
void setup() {
pinMode(BUTTON_PIN, INPUT); // Set button pin
servoMotor.attach(SERVO_PIN);
servoMotor.write(0); // Start at 0 degrees
}
void loop() {
if (digitalRead(BUTTON_PIN) == LOW) { // Button pressed
if (!buttonPressed) { // Only trigger once per press
buttonPressed = true;
rotateServoTwice();
}
} else {
buttonPressed = false; // Reset when button is released
}
}
void rotateServoTwice() {
for (int i = 0; i < 2; i++) { // Move 0 → 180 → 0 two times
servoMotor.write(180);
delay(500); // Allow time to reach position
servoMotor.write(0);
delay(500); // Allow time to return
}
}
REWARD MACHINE SCHEMATICS AND PCB BOARD
OUTPUD DEVICES CODES (TESTED WITH TREADMILL PCB)
SERVO MOTOR CODE (ARDUINO IDE)
#include <ESP32Servo.h>
#define LED_PIN D7
#define BUTTON_PIN D9
Servo myservo; // create Servo object to control a servo
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
myservo.attach(D6); // attaches the servo on pin D6 to the Servo object
}
void loop() {
if (digitalRead(BUTTON_PIN) == LOW) { // Check if button is pressed
for (int i = 0; i < 3; i++) { // Repeat the cycle 3 times
digitalWrite(LED_PIN, HIGH); // Turn LED on
myservo.write(0);
delay(300);
myservo.write(180);
delay(300);
}
digitalWrite(LED_PIN, LOW); // Turn LED off
}
}
NEO PIXEL LED STRIP WS2812B CODE (ARDUINO IDE)
#include <Adafruit_NeoPixel.h>
#define LED_PIN D6
#define NUM_LEDS 38 // Number of LEDs in the strip
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
void setup() {
strip.begin();
strip.show(); // Initialize all pixels to 'off'
}
void loop() {
rainbowEffect(); // Always run the rainbow effect
}
void rainbowEffect() {
uint16_t i, j;
for (j = 0; j < 256; j++) {
for (i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i + j) & 255));
}
strip.show();
delay(20);
}
}
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
9. INPUT DEVICES (WEEK09)
TREADMILL SCHEMATICS AND PCB BOARD
TREADMILL CODE
HALL SENSOR + LED (ARDUINO IDE)
#define HALL_SENSOR_PIN D6 // White wire (signal) from the Hall sensor
#define LED_PIN D7 // LED connected to D7
void setup() {
pinMode(HALL_SENSOR_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
}
void loop() {
int sensorState = digitalRead(HALL_SENSOR_PIN); // Read Hall sensor
if (sensorState == LOW) {
digitalWrite(LED_PIN, HIGH); // Turn ON LED when magnet is detected
} else {
digitalWrite(LED_PIN, LOW); // Turn OFF LED when no magnet
}
delay(50); // Small delay for stability
}
HALL SENSOR + LED + PUSH BUTTON TOGGLE (ARDUINO IDE)
#define HALL_SENSOR_PIN D6
#define LED_PIN D7
#define BUTTON_PIN D9
void setup() {
pinMode(HALL_SENSOR_PIN, INPUT);
pinMode(BUTTON_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200); // Start Serial Monitor
}
void loop() {
int sensorState = digitalRead(HALL_SENSOR_PIN); // Read Hall sensor
int buttonState = digitalRead(BUTTON_PIN); // Read button state
if (sensorState == LOW || buttonState == LOW) {
digitalWrite(LED_PIN, HIGH); // Turn LED ON
Serial.println("LED ON");
} else {
digitalWrite(LED_PIN, LOW); // Turn LED OFF
Serial.println("LED OFF");
}
// Print sensor and button states for debugging
Serial.print("Hall Sensor: ");
Serial.println(sensorState == LOW ? "ON" : "OFF");
Serial.print("Button: ");
Serial.println(buttonState == LOW ? "PRESSED" : "NOT PRESSED");
delay(300); // Small delay to avoid spam in Serial Monitor
}
8. PCD PRODUCTION (WEEK08)
CAT’S PAW BUTTON & REWARD MACHINE PCB BOARDS
CAT’S PAW BUTTON CODE
I made again same circuit in Wokwi and edited my code to show ON and OF in the serial monitor
PUSH BUTTON (pull up resistor) + LED (ARDUINO IDE)
// Define pin numbers for button and LED
const int button = D9; // Button pin (input)
const int led = D7; // LED pin (output)
void setup() {
// Start the serial communication at 115200 baud rate
Serial.begin(115200);
// Set the button pin as input (with external pull-up resistor)
pinMode(button, INPUT);
// Set the LED pin as output to control the LED state
pinMode(led, OUTPUT);
}
void loop() {
// Read the button state (HIGH = not pressed, LOW = pressed)
bool buttonState = digitalRead(button);
// Invert the button state to control the LED (LED on when button is pressed)
digitalWrite(led, !buttonState);
// Print the current state of the LED to the serial monitor (ON or OFF)
Serial.println(buttonState ? "OFF" : "ON");
// Small delay to debounce the button and avoid multiple reads in quick succession
delay(100);
}
REWARD MACHINE CODE
I did the same for my Reward Machine PCB
PUSH BUTTON (pull down resistor) + LED (ARDUINO IDE)
// Define pin numbers for button and LED
const int button = D9; // Button pin (input)
const int led = D6; // LED pin (output)
void setup() {
// Start serial communication at 115200 baud rate
Serial.begin(115200);
// Print "Ready" to the Serial Monitor to indicate setup is complete
Serial.println("Ready");
// Set the button pin as input (with pull-down resistor)
pinMode(button, INPUT);
// Set the LED pin as output to control the LED state
pinMode(led, OUTPUT);
// Start with the LED turned off
digitalWrite(led, LOW);
}
void loop() {
// Read the current state of the button
// Returns HIGH when the button is pressed, LOW when it is not pressed
bool buttonState = digitalRead(button);
// Set the LED state based on the button state
// The LED will turn on when the button is pressed (HIGH), off when not pressed (LOW)
digitalWrite(led, buttonState);
// Print the current state of the LED to the Serial Monitor
// Print "ON" if the button is pressed, "OFF" if it is not
Serial.println(buttonState ? "ON" : "OFF");
// Small delay to debounce the button and prevent multiple reads in quick succession
delay(100);
}
7. COMPUTER-CONTROLLED MACHINING (WEEK07)
TREADMILL 3D MODEL
I made my Treadmill parametric model from previous weeks more detailed.
and created first CNC cut prototype with bearings, rods and 3D printed conical wheels
They don’t feel stable, so I need to work more on the design…
6. ELECTRONIC DESIGNS (WEEK06)
CAT’S PAW BUTTON CIRCUIT UPDATE
My previous simulation in Wokwi had a small error:
- Xiao ESP32-C3 pins only support 3.3V and ****using 5V could damage the microcontroller.
Here’s the corrected schematic:
CAT’S PAW BUTTON SCHEMATICS & VISUALISATION
REWARD MACHINE SCHEMATICS & VISUALISATION
Here’s the first approach to the PCB design and its visualisation (where I was expecting to see a real buzzer since in schematic I can see a speaker icon not a pin out, but maybe that’s better, I can place the speaker further from the board)
5. 3D PRINTING (WEEK05)
CAT’S PAW BUTTON 3D MODEL
I tried some new features in Fusion360 called FROM MODELING, and here’s my first look of the remote button:
CAT’S PAW BUTTON PRINTS
I Printed the bottom part on the BambuLab and the middle one on the Prusa.
I tried also a resin printer with a rubber material, unfortunately I could get proper outcome:
4. EMBEDED PROGRAMMING (WEEK04)
CAT’S PAW BUTTON WOKWI SYMULATION
CREDIT: Francisco Ruz (code edited for my purposes)
CAT’S PAW BUTTON CODE
BUTTON + LED (ARDUINO IDE)
const int buttonRed = D9; // **Defines pin D9** as the button input
const int ledRed = D2; // **Defines pin D2** as the LED output
void setup() {
Serial.begin(115200); // Start Serial Monitor
Serial.println("Ready"); // Prints "Ready"
pinMode(buttonRed, INPUT); //Button not pressed = pin reads HIGH (due to the pull-up resistor)
pinMode(ledRed, OUTPUT); // Set LED pin as OUTPUT
digitalWrite(ledRed, LOW); // Start with LED OFF
}
void loop() {
bool redState = digitalRead(buttonRed); //reads HIGH or LOW
digitalWrite(ledRed, !redState); //controls LED by setting it to HIGH (on) or LOW (off)
Serial.print("Red: ");
Serial.print(!redState); //Prints the LED states to the Serial Monitor
delay(100); // **100ms delay** to debounce the button.
}
3. COMPUTER-AIDED CUTTING (WEEK03)
TREADMILL 3D MODEL
I learned more about parametric design and created fully adjustable model of my Treadmill:
2. COMPUTER-AIDED DESIGN (WEEK02)
REWARD MACHINE 3D MODEL
My first attempt at using Fusion 360 and creating a 3D model of the Reward Machine.
1. PROJECT MANAGEMENT (WEEK01)
CONCEPT
FitnessCat is a cat gym that consists of several smart devices. The primary component is a food dispenser that provides exercise rewards (ideally low-calorie snacks). This device works wirelessly with three additional exercise machines:
- Wireless Button: A simple wireless button that triggers snack release when pressed. The idea is to place it far enough so the cat must travel a certain distance to earn the reward.
- Climbing Tower or Wall: This device recognizes when the cat climbs to the top, signalling progress with lights and sounds. Upon reaching the top, the cat is rewarded with a treat.
- Treadmill Wheel: Equipped with a distance sensor, this device can be programmed to specific goals (e.g., 100 meters). Like the other devices, it signals progress and dispenses a reward once the target is achieved.
- Future Expansions: There are possibilities to extend the system with additional types of devices.
The reward system uses signals like lights and sounds that are easily recognized by cats but not disruptive to humans, ensuring the device remains functional without being annoying.
The idea came from noticing that two of my cats clearly get far too little exercise during the day. Naturally, they tend to be more active at night, which is when humans rest. The goal is to create a smart device that encourages cats to move and burn calories at night- without disturbing their owners.