Pi-Final Project Documentation : Pi’s Moving Castle

Pi’s moving Castle is a p5js + Arduino interactive game by Pi.

Pi has a moving castle, but it is broken down by the presence of rust gremelins in the machinery. You have to help Pi eliminate these gremelins so that the castle can walk again.

Here’s a more visually appealing version of the story.

Documentation Video

instagrammable goodies

DeMo & Concept

So the project consists of 2 parts.  A p5js computer game, and a castle with walking legs and some user inputs (potentiometer and a switch). You control a cannon on the computer screen, using the potentiometer to rotate the cannon and shoot it with the switch. But there is a catch, Some of the gremelins, you cannot aim at them directly, so you need to make the cannonballs bounce off the walls to deliver justice to these monsters. Finally once you have cleared all the monsters, the castle can start walking and will physically walk. Below is a demo video of me playing the full experience.

Arduino Code

The arduino code is below. It always send back serial data with the potentiometer and switch readings back to the computer, and it will wait for a single serial int. If computer sends a 1, castle walks, and if computer sends a 0, it stops walking. Depending on the game state it changes.

#include   // Include the Servo library

Servo myServo;  // Create a servo object
Servo myServo2; // Create a servo object
float lastVoltage = -1; // Variable to store the last voltage
// Arduino code for button, which detects the counts
const int buttonPin = 2;  // the number of the pushbutton pin
const int ledPin = 3;    // the number of the LED pin

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status
int lastButtonState = HIGH;  // variable for reading the last pushbutton status
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers
int pressCount = 0;  // count of button presses


//Potentiometer
float floatMap(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}


void setup() {
  pinMode(ledPin, OUTPUT);   // initialize the LED pin as an output
  pinMode(buttonPin, INPUT_PULLUP);  // initialize the pushbutton pin as an input with internal pull-up resistor
  myServo.attach(9);  // Attach the servo signal pin to digital pin 9
  myServo2.attach(10); // Attach the servo signal pin to digital pin 10
  Serial.begin(9600); // Initialize serial communication at 9600 bits per second
  stopRotation(); // Stop servos by default
}

void loop() {
   int reading = digitalRead(buttonPin);

  //Output potentiometer
  // Read the input on analog pin A0:
  int analogValue = analogRead(A0);
  // Rescale to potentiometer's voltage (from 0V to 5V):
  float voltage = floatMap(analogValue, 0, 1023, 0, 5);
 

  // check if the button state has changed from the last reading
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // only toggle the LED if the new button state is LOW
      if (buttonState == LOW) {
        digitalWrite(ledPin, HIGH);
        pressCount++;  // increment the press count
      } else {
        digitalWrite(ledPin, LOW);
      }
    }
  }

  // save the reading. Next time through the loop, it will be the lastButtonState:
  lastButtonState = reading;
 
  Serial.print(pressCount);  // print the count to the serial monitor
  Serial.print(",");
  Serial.println(voltage);          // Print the distance to the Serial monitor
  delay(100);                        // Short delay before next measurement

 
  if (Serial.available() > 0) { // Check if data has been received
    int state = Serial.read() - '0'; // Read the first byte available and convert from ASCII
    if (state == 1) {
      rotate(); // Rotate servos
    } else if (state == 0) {
      stopRotation(); // Ensure servos are stopped
    }
  }
}

void rotate() {
  myServo.writeMicroseconds(4000); // Example value for rotation
  myServo2.writeMicroseconds(4000); // Adjust if necessary
}

void stopRotation() {
  myServo.writeMicroseconds(1500); // 1500 usually represents a stopped servo
  myServo2.writeMicroseconds(1500); // Adjust if necessary
}

p5js Sketch

In the game, players help Pi clear rust gremlins from a mechanical castle using a turret that shoots cannonballs, controlled by a physical potentiometer and switch. The game mechanics include obstacles where cannonballs need to be bounced off boundaries to hit some gremlins. The game features a visual and auditory loading sequence with gremlin and turret images, background music, and sound effects for actions like shooting and gremlin deaths. The Arduino setup facilitates interaction by receiving turret control signals from the potentiometer and switch, while sending back movement commands to make the castle walk when the game is completed.

The embedding of the p5js sketch is below (Note that you need the castle to play the game).

Communication between Arduino and p5js

As mentioned above, the communication between p5js and Arduino is serial data. Arduino sends 2 values (a float reading for potentiometer, and an int counting the number of times the button has been clicked). This controls the rotation of the cannon and firing of the cannon in the game.

From the computer (p5), Arduino only receives one number all the time that is either 1 or 0. This dictates whether or not to move the castle and make it walk (it walks when the game is complete.)

What I am proud of

I am particularly very proud of the visual design, the storyline and the walking mechanism. This looks almost unreal to me, I was not expecting that sticking the midjourney textures on an Amazon cardboard box would look sooo good.

Future Improvements

For future improvements, I will integrate what the users have told me during the user tests.

Leave a Reply