Skip to content

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

presentation.png

••••••••••••••••••••••••••••••

FINAL PRODUCTS

✅CAT’S PAW BUTTON

buttonrender.jpg

buttonexplode.jpg

FILES

STL

✅REWARD MACHINE

rewardrender.jpg

rewardexplode.jpg

FILES

STL

✅TREADMILL

tredmillrender.jpg

treadmillexplode.jpg

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

kicadbutton.jpg

kicadbutton2.jpg

FILES

KICAD

✅REWARD MACHINE

rewardfinalschematics.jpg

rewardfinalpcb.jpg

FILES

KICAD

✅TREADMILL

treadmillfinalschematics.jpg

treadmillfinallpcb.jpg

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).

licence.jpg

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

rewardsection.jpg

TREADMILL PROTOTYPE UPDATE

The new side panels

newsidepanel.jpg

newsidepanel2.jpg

newsidepanel3.jpg

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

buttonfinalmodel.jpg

buttonfinalmodel2.jpg

buttonfinalmodel3.jpg

Wiring, PCB mount and button mechanic system

buttonfinalprototype.jpg

There was lots of prototyping of the button itself, that was keep braking. Here’s the final section through the button system

buttonfinalmodel4.jpg

Final prints:

buttonfinalprototype2.jpg

Bottom lid has some ant slippery pads

FINAL PRODUCT

15. SYSTEM INTEGRATION (WEEK16)

CAT’S PAW BUTTON FINAL PCB

kicadbutton.jpg

kicadbutton2.jpg

pcbbuttonfinal.jpg

pcbbuttonfinal2.jpg

CAT’S PAW BUTTON 3D MODEL & PROTOTYPING UPDATE

buttonmodel.jpg

buttonmodel2.jpg

buttonmodel3.jpg

buttoncable.jpg

TREADMIL 3D MODEL & PROTOTYPING UPDATE

treadmillmodel.jpg

treadmillmodel2.jpg

treadmillmodel3.jpg

rodes.jpg

crosses.jpg

nt2.jpg

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

buttonmold.jpg

buttonmold2.jpg

12. MACHINE BUILDING (WEEK12/13)

****I build a plotter that could be an extension to my FinessCat

idea.jpg

11. NETWORKING & COMMUNICATION (WEEK11)

SYSTEM SCHEMATIC

diagram.jpg

CAT’S PAW BUTTON NEW PCB BOARD

Unfortunately I overlooked a mistake I did in THE REWARD MACHINE’s PCB and I repeated it:

xiao.jpg

kikadwrong0.jpg

kikadwrong.jpg

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.

buttonpcbthiny.jpg

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

pinheaderbroken.jpg

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

kikadreward.jpg

kikadreward2.jpg

pcb.jpg

pcbBack.jpg

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

kikad0.jpg

kikad.jpg

pcb.jpg

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

soldering3.jpg

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

WOKWI.jpg

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

WOKWI2.jpg

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.

treadmill.jpg

and created first CNC cut prototype with bearings, rods and 3D printed conical wheels

t6.jpg

t8.jpg

t9.jpg

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:

kicad_button_wokwi.jpg

CAT’S PAW BUTTON SCHEMATICS & VISUALISATION

kicad_button_schemat.jpg

kicad_button_pcb.jpg

kicad_button_3d.jpg

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)

kicad_RM_schemat.jpg

kicad_RM_pcb.jpg

kicad_RM_3d.jpg

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:

FINALBUTTON.jpg

FINALBUTTON2.jpg

CAT’S PAW BUTTON PRINTS

I Printed the bottom part on the BambuLab and the middle one on the Prusa.

button_print.jpg

I tried also a resin printer with a rubber material, unfortunately I could get proper outcome:

almost5.jpg

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:

PARAMETER_SKETCH.jpg

PARAMETER_SKETCH2.jpg

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.

MY_PROJECT10.jpg

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.

SKETCHES

FPsketch_result.jpg

FPsketch2a_result.jpg

FPsketch2b_result.jpg