SnapBow

SnapBow

A 3D-printed bow bluetooth game controller

Arduino Unity 3D-Printing

Concept

The aim of this project was to create a game controller, which allows shooting a bow by pulling on a string attached to the controller. Another idea which gave this project its name is, that this controller could be snapped onto the back of an iPhone to achieve a better immersion. More on that later.

Connection Methods

Attempt #1 - Arduino Pro Micro via USB

The first idea was to use an Arduino Pro Micro to emulate an HID USB game controller on the iPhone. After receiving the Arduino Pro Micro, I tested emulating a game controller successfully with the following library on a PC: GitHub - gamelaster/ArduinoGamepad: A GamePad HID library for Arduino Pro Micro/Leonardo (ATMega32u4)

The assumption was, that with the switch to USB-C on the latest iPhone, any HID USB device would work. But no, that wouldn’t be the Apple way of doing things. So any USB device requires a special MFi (Made For iPhone/iPod)

Attempt #2 - Arduino ESP32 via Bluetooth

The next attempt was to use an Arduino Nano ESP32, which is capable of Bluetooth Low Energy to connect to the iPhone. The library I used for this can be found here: GitHub - lemmingDev/ESP32-BLE-Gamepad: Bluetooth LE Gamepad library for the ESP32

Connecting to a Mac worked with no issues. Trying to connect to the iPhone, again brought some kind of connection timeout. After reading a lot of forum posts, I saw that it may work by emulating the Device ID, the Manufacturer ID as well as the Firmware Revision of an Xbox One Controller. I was able to do that with the following code:

config->setVid(0x045E);
config->setPid(0x0B13);
config->setFirmwareRevision("5.9.2709.0");

Now the controller was able to connect to the iPhone and was even shown as an Xbox Controller in the Game Controller Settings in iOS. This would suggest the controller now works, but the small button under the controller (“Identify controller”) meant that - even though the controller was connected - the (non-existent) MFi chip was not able to communicate with the phone. Because of this, the controller input wasn’t accepted by the phone. In the end, the decision was, to restructure the project, to not focus on the iPhone but make a generic Bluetooth controller that can connect to any other device. This also meant that the controller would no longer be snapped to the back of the phone but rather just be held in the hand.

Hardware

Attempt #1 - Joysticks

The first idea to measure the force of pulling the string, was to use two joysticks, one on top and one on the bottom with the string connected to both. This would even make it possible to measure if the string would be pulled downwards or upwards additionally to the force itself. When testing this approach, the initial force required was quite high and the difference between 0% and 100% input was a very small gap. This was not suitable for pulling stronger.

Attempt #2 - Load Cell

The second idea was to use a load cell, which is usually used in scales to measure weights. The bow string is going through the outermost holes and is then glued to the back of the cell itself. The load cell used supports up to 5kg and uses a HX711 analog/digital converter. Even though the cell is pulled in the same direction on both sides, it can still measure the stretch at the thin points (the white glued part on the top and bottom of the sensor). This worked surprisingly well, with only a small delay which is caused by the deformation of the cell itself. The load cell values were mapped to the right trigger of a normal controller layout. Since the sensor values of the load cell were highly dependent on its tare value when starting, the program waits for a few seconds before calibrating the load cell. The given value from the load cell is multiplied by 25 and then capped from 0 to 32786, which is the range for a trigger in the BLE-Gamepad Arduino library.

The String

With a normal bow, it’s not the string that is stretched but rather the wood bow itself. With the loading cell made from metal, the stretching part has to be handled by the string alone. After trying multiple different wool strings (not enough stretch) and stretchy strings (too much stretch, which manipulated the measurements). The final string used was a sock string made from pure new wool and polyamide, which had the ideal amount of stretch and durability. To further enhance the durability three strings were weaved together.

The Magnet

Before finding out that iOS does not support any custom gamepads, I ordered magnets to be able to snap the controller to the back of the phone. I bought these wall mounting magnets and cut one of them open to take the magnet out. The magnet is actually made from multiple rounded magnets aligned on a metal disc.

Unity - Custom Game Controller

To demonstrate the bow mechanics, I created a compact Unity game that utilizes a line renderer. It adjusts the position of the line’s midpoint based on the strength input from the controller, showcasing the bow’s tension. To identify the gamepad, the following snippet was used (still using the Xbox Controllers Vendor and Manufacturer ID):

InputSystem.RegisterLayout<ESP32Gamepad>(matches:
   new InputDeviceMatcher()
       .WithInterface("HID")
       .WithProduct("ESP32 BLE Gamepad")
       .WithManufacturer("Microsoft")
       .WithCapability("vendorId", 0x045E)
       .WithCapability("productId", 0x0B13));

As the Arduino delivers a different byte order, there was an additional need to define a new Input Report for the controller:

[StructLayout(LayoutKind.Explicit, Size = 32)]
struct ESP32HIDInputReport : IInputStateTypeInfo
{
   public FourCC format => new FourCC('H', 'I', 'D');

   [InputControl(name = "rightTrigger", format="SHRT")]
   [FieldOffset(5)] public byte rightTrigger;
}

To find out the byte field offset and overall size of the payload sent by the Arduino, I used the Unity Input Debugger provided by the new Input System.

3D-printed case

As already mentioned above, the concept was changed short-term from a snap-on controller for the back of a phone to a more generic handheld controller. To keep support structures to a minimum, the whole case is based on a cylinder. The load cell is supported on all sides tightly, so it stays in place and delivers consistent values. On each side of the back-facing support post, there is space for the Arduino Nano as well as the HX711 ADC.

Printing the case took about 3 hours with a Coarse resolution of 0.4mm. This resolution was picked to improve the strength of the outer shell. Putting it all together, the most challenging part was to get the string through the hole at the bottom. After that, the string was hot glued to the back of the load cell to stay in place.

©️ 2024 | Built with ❤️ by Felix Beer