Concept
For the midterm project, the goal was to create a simple interactive system with physical controls. I partnered with Sammy Sords, who also works as stage combatant and teaches stage fighting with broadswords. We pursued the idea of pairing a broadsword with the Arduino and a speaker to teach sword fight parry positions and pacing. The tone would change upon the orientation or clash of the sword, and altogether, the movements would generate a song.
Process
We made use of the Arduino Nano 33 IoT’s built-in LSM6DS3 inertial motion unit (IMU), which is a 3-axis accelerometer and 3-axis gyroscope. We installed the Arduino LSM6DS3 library to read the values for acceleration and rotation and the Madgwick library to further determine the heading, pitch, and roll for the parry positions.
The code was based on the example provided for Determining Orientation. Sammy mounted the Arduino onto the sword and defined the orientation for four different parry positions (1st, 2nd, 3rd, and 4th) based on the serial monitor readings, and then we assigned a tone frequency to each position.
While we planned to also generate sound when the sword clashed with another sword, we weren’t able to resolve the issue of the gyroscope bouncing on impact, and so it was not part of the final result.
#include <Arduino_LSM6DS3.h> #include <MadgwickAHRS.h> // initialize a Madgwick filter: Madgwick filter; // sensor's sample rate is fixed at 104 Hz: const float sensorRate = 104.00; void setup() { Serial.begin(9600); // attempt to start the IMU: if (!IMU.begin()) { Serial.println("Failed to initialize IMU"); // stop here if you can't access the IMU: while (true); } // start the filter to run at the sample rate: filter.begin(sensorRate); } void loop() { // values for acceleration & rotation: float xAcc, yAcc, zAcc; float xGyro, yGyro, zGyro; // values for orientation: float roll, pitch, heading; // check if the IMU is ready to read: if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()) { // read accelerometer & gyrometer: IMU.readAcceleration(xAcc, yAcc, zAcc); IMU.readGyroscope(xGyro, yGyro, zGyro); // update the filter, which computes orientation: filter.updateIMU(xGyro, yGyro, zGyro, xAcc, yAcc, zAcc); // print the heading, pitch and roll roll = filter.getRoll(); pitch = filter.getPitch(); heading = filter.getYaw(); Serial.print("Orientation: "); Serial.print(heading); Serial.print(" "); Serial.print(pitch); Serial.print(" "); Serial.println(roll); // play tone for parry position -- 1st if (xGyro > 80 && pitch <= 2 && roll >= 5) { tone(8, 523.25, 1000); delay(100);} // play tone for parry position -- 2nd else if (xGyro > 80 && pitch <=2 && roll < 5) { tone(8, 587.33, 1000); delay(100);} // play tone for parry position -- 3rd else if (xGyro > 80 && pitch > 2 && roll >= -5) { tone(8, 659.25, 1000); delay(100);} // play tone for parry position -- 4th else if (xGyro > 80 &&pitch > 2 && roll < -5) { tone(8,698.46,1000); delay(100);} } }
Result
For the demonstration, Sammy attached the speaker to the glove of hand bearing the sword and powered the Arduino with a portable USB charger. The volume of the speaker ended up being too quiet during the in-class demo (which could be addressed in the future), but the tone output can be heard in the video above and fulfills our goal of providing sound feedback to the user.