26 November 2023

Happy Wheels Segway Guy Controller









I based my controller on the game Happy Wheels. For my project I specifically designed it to play as the Segway Guy as I felt it would be the easiest character/vehicle to conceptualize into a budget friendly controller. However, this controller is compatible with every character. I used a pogo stick to emulate a segway because I'd be able to tilt it directionally in a similar fashion to how a segway is operated. In addition, the pogo stick has similar size and handles to a segway. Attached to the pogo stick is a tree ornament with the Circuit Playground Bluefruit inside of it. This way tilting the pogo stick would tilt the CPB to utilize its accelerometer function for movement controls. Tilting the pogo stick forward pushes the up arrow, tilting back pushes the down arrow, and then tilting left and right pushes those respective arrow keys. As an external sensor, I have a single pull double throw switch that makes the player jump by pressing the space bar when flicked upwards.

My question for my peers would be "If you were to design a controller for Happy Wheels which character would you pick and how would you design it prioritizing cost efficiency?" For context I spent $25 on this project between the pogo stick and ornament.


#include <Adafruit_TinyUSB.h>
#include <Adafruit_CircuitPlayground.h>
#include <Adafruit_Circuit_Playground.h>

//HID report descriptor using TinyUSB's template
//Single report (no ID) descriptor
uint8_t const desc_hid_report[] = {
  TUD_HID_REPORT_DESC_KEYBOARD()
};

// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_KEYBOARD, 2, false);
//https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h

// Report payload defined in src/class/hid/hid.h
// - For Gamepad Button Bit Mask see  hid_gamepad_button_bm_t
// - For Gamepad Hat    Bit Mask see  hid_gamepad_hat_t
hid_gamepad_report_t gp;

float X, Y, Z;
int uppin;        //3.3v
int midpin;       //gnd
int downpin = 6;  //A6

void setup() {
  // put your setup code here, to run once:
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
  // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
  TinyUSB_Device_Init(0);
#endif
  Serial.begin(115200);
  CircuitPlayground.begin();
  pinMode(uppin, INPUT_PULLUP);    //switch up
  pinMode(midpin, INPUT_PULLUP);   //switch mid?
  pinMode(downpin, INPUT_PULLUP);  //switch down
  usb_hid.begin();
  // wait until device mounted
  while (!TinyUSBDevice.mounted()) delay(1);

  Serial.println("Adafruit TinyUSB HID Gamepad example");
}

void loop() {
  // put your main code here, to run repeatedly:
  // Read accelerometer value
  X = CircuitPlayground.motionX();
  Y = CircuitPlayground.motionY();
  Serial.print("  X: ");
  Serial.print(X);
  Serial.print("  Y: ");
  Serial.print(Y);
  Serial.print("  Z: ");
  Serial.println(Z);

  delay(1000);
  //  // Remote wakeup
  //  if ( TinyUSBDevice.suspended() && btn )
  //  {
  //    // Wake up host if we are in suspend mode
  //    // and REMOTE_WAKEUP feature is enabled by host
  TinyUSBDevice.remoteWakeup();
  //  }
  if (!usb_hid.ready()) return;
  //Keyboard report, ID, modifier, and keycodes
  uint8_t const report_id = 0;  //unsigned 8bit (1 byte) int
  uint8_t modifier = 0;         //used by libraries to save space
  uint8_t keycode[6] = { 0 };   //normal int is 16 bit (4 bytes)

  // Adjust these values based on your sensor orientation
  int pitch = map(Y, -30, 30, -45, 45);
  int roll = map(X, -30, 30, -45, 45);

  if (Y > 5) {
      keycode[0] = HID_KEY_ARROW_UP;
      Serial.print(" UP ");
      usb_hid.keyboardReport(report_id, modifier, keycode);
    } else if ( Y < -5) {
      keycode[0] = HID_KEY_ARROW_DOWN;
      Serial.print(" DOWN ");
      usb_hid.keyboardReport(report_id, modifier, keycode);
    }

  if (X > 5) {
      keycode[0] = HID_KEY_ARROW_RIGHT;
      Serial.print(" RIGHT ");
      usb_hid.keyboardReport(report_id, modifier, keycode);
    } else if (X < -5) {
      keycode[0] = HID_KEY_ARROW_LEFT;
      Serial.print(" LEFT ");
      usb_hid.keyboardReport(report_id, modifier, keycode);
    }

  if (Y < 5 && Y > -5)
  {
    usb_hid.keyboardRelease(HID_KEY_ARROW_UP);
    usb_hid.keyboardRelease(HID_KEY_ARROW_DOWN);
  }

  if (X < 5 && X > -5)
  {
    usb_hid.keyboardRelease(HID_KEY_ARROW_LEFT);
    usb_hid.keyboardRelease(HID_KEY_ARROW_RIGHT);
  }

  int state1 = digitalRead(uppin);
  int state2 = digitalRead(midpin);
  int state3 = digitalRead(downpin);
  if (state1 == 1) {
    keycode[0] = HID_KEY_SPACE;
    Serial.print(" SPACE ");
    usb_hid.keyboardReport(report_id, modifier, keycode);
    delay(10);
    usb_hid.keyboardRelease(0);
  }

  if (state2 == 0) {
    keycode[0] = HID_NULL_STATE;
    Serial.print(" NULL ");
    usb_hid.keyboardReport(report_id, modifier, keycode);
    delay(10);
    usb_hid.keyboardRelease(0);
  }

  if (state3 == 2) {
    keycode[0] = HID_KEY_Z;
    Serial.print(" Z ");
    usb_hid.keyboardReport(report_id, modifier, keycode);
    delay(10);
    usb_hid.keyboardRelease(0);
  }
}
 

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.